Skip to main content

rucc_tuple/
table.rs

1//! The target table: which targets exist, what is true of each one today, and what this
2//! specification is committed to making true of it.
3//!
4//! This is `spec/cross-compile/04-target-matrix.md` section 4.3 as data. It is the plan rather than the
5//! report: section 4.7 is explicit that a tier is computed from corpus results by the reporting
6//! job and never asserted by a human, so the `tier` column here is what the last reporting run
7//! established and the `planned` column is the commitment. Raising a `tier` in this file
8//! without a corpus run behind it is the one edit that makes the whole table worthless.
9//!
10//! The spec writes `*-none` as a single row covering every architecture. It is expanded here,
11//! and the expansion caps each freestanding row at the tier its architecture reaches when
12//! hosted, because a freestanding target and a hosted one share a back end and the freestanding
13//! one cannot be better tested than the code generator underneath it.
14
15use core::fmt;
16use core::str::FromStr;
17
18use crate::{Error, TargetTuple};
19
20/// What is known to be true about a target.
21///
22/// The ordering is the useful one: `Tier::Supported` is the smallest, so `min` picks the better
23/// of two tiers and a sorted list starts with the best supported targets.
24#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub enum Tier {
26    /// Rungs 0, 1 and 2 pass on hardware at every optimization level, the ABI differential is
27    /// green against GCC, code quality is measured and within bound, and every hardening flag
28    /// works.
29    Supported,
30    /// Rungs 0 and 1 pass, on hardware or under qemu-user, the ABI differential is green, a
31    /// sysroot ships or fetches, and code quality is measured and published whatever it says.
32    SupportedWithEvidence,
33    /// Objects are emitted and a linked program runs the smoke test. No rung passes yet.
34    InProgress,
35    /// The tuple parses and `--print-config` is correct. Nothing is emitted.
36    ///
37    /// This tier exists so that a user asking for a target we have no back end for is told
38    /// exactly that, with an issue number, rather than being told the compiler has never heard
39    /// of their machine. Those two messages lead a user to opposite conclusions.
40    Recognized,
41}
42
43impl Tier {
44    /// The number, which is how the spec and every issue refer to these.
45    pub const fn number(self) -> u8 {
46        match self {
47            Tier::Supported => 1,
48            Tier::SupportedWithEvidence => 2,
49            Tier::InProgress => 3,
50            Tier::Recognized => 4,
51        }
52    }
53
54    /// The words the README uses for this tier, which are deliberately weaker than the tier
55    /// number as the tier gets worse.
56    pub const fn describe(self) -> &'static str {
57        match self {
58            Tier::Supported => "supported",
59            Tier::SupportedWithEvidence => "supported, with the evidence column saying how",
60            Tier::InProgress => "in progress, named in the table, not claimed",
61            Tier::Recognized => "recognized",
62        }
63    }
64
65    /// Whether a program for this target can be compiled and linked today.
66    ///
67    /// True for tiers 1, 2 and 3. `spec/cross-compile/04-target-matrix.md` measures claim 1 against the count
68    /// of rows for which this holds.
69    pub const fn compiles_and_links(self) -> bool {
70        !matches!(self, Tier::Recognized)
71    }
72}
73
74impl fmt::Display for Tier {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        write!(f, "{}", self.number())
77    }
78}
79
80/// Whether rucc itself runs on this target, which is a separate and much cheaper question from
81/// whether rucc compiles for it.
82#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
83pub enum Host {
84    /// A release artifact is built for it and CI runs on it.
85    Yes,
86    /// Planned, but not yet a runner.
87    Planned,
88    /// Not a host, and not intended to be one.
89    No,
90}
91
92impl Host {
93    /// The word the table prints.
94    pub const fn as_str(self) -> &'static str {
95        match self {
96            Host::Yes => "yes",
97            Host::Planned => "later",
98            Host::No => "no",
99        }
100    }
101}
102
103impl fmt::Display for Host {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        f.write_str(self.as_str())
106    }
107}
108
109/// One row of the target table.
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub struct TargetEntry {
112    /// The canonical spelling. Every entry parses back to itself, which the tests check,
113    /// because a table whose keys are not canonical is a cache with two entries per target.
114    pub tuple: &'static str,
115    /// What the last reporting run established.
116    pub tier: Tier,
117    /// What the plan in `spec/cross-compile/15-plan.md` commits to.
118    pub planned: Tier,
119    /// Whether rucc runs on this target.
120    pub host: Host,
121    /// Why this row is in the table, or what makes it awkward. Taken from the spec.
122    pub note: &'static str,
123}
124
125impl TargetEntry {
126    /// Parse the row's tuple.
127    ///
128    /// # Errors
129    ///
130    /// Only if the table itself is wrong, which the tests are there to prevent.
131    pub fn parse(&self) -> Result<TargetTuple, Error> {
132        TargetTuple::from_str(self.tuple)
133    }
134}
135
136/// Every target the compiler has an opinion about.
137///
138/// Adding a row here is the entry price for `spec/cross-compile/02-the-goal.md` claim 3: bringing up a target
139/// should be a data change, and this is the data. A row that needs code outside the target crate
140/// is a row that has found a leak in the abstraction, and the leak is the bug rather than the
141/// row.
142pub const TARGETS: &[TargetEntry] = &[
143    // Linux.
144    TargetEntry {
145        tuple: "x86_64-linux-gnu",
146        tier: Tier::SupportedWithEvidence,
147        planned: Tier::Supported,
148        host: Host::Yes,
149        note: "the reference target, and everything is measured here first",
150    },
151    TargetEntry {
152        tuple: "x86_64-linux-musl",
153        tier: Tier::Recognized,
154        planned: Tier::Supported,
155        host: Host::Yes,
156        note: "the easiest sysroot in existence, and the first cross target",
157    },
158    TargetEntry {
159        tuple: "aarch64-linux-gnu",
160        tier: Tier::Recognized,
161        planned: Tier::Supported,
162        host: Host::Yes,
163        note: "char is unsigned here and signed on x86-64, which is the classic bug",
164    },
165    TargetEntry {
166        tuple: "aarch64-linux-musl",
167        tier: Tier::Recognized,
168        planned: Tier::Supported,
169        host: Host::Yes,
170        note: "the pair that proves the libc and the architecture are separate axes",
171    },
172    TargetEntry {
173        tuple: "riscv64-linux-gnu",
174        tier: Tier::Recognized,
175        planned: Tier::Supported,
176        host: Host::Planned,
177        note: "the middle end canary, with no condition codes and no complex addressing",
178    },
179    TargetEntry {
180        tuple: "riscv64-linux-musl",
181        tier: Tier::Recognized,
182        planned: Tier::SupportedWithEvidence,
183        host: Host::No,
184        note: "",
185    },
186    TargetEntry {
187        tuple: "i686-linux-gnu",
188        tier: Tier::Recognized,
189        planned: Tier::SupportedWithEvidence,
190        host: Host::No,
191        note: "x87 long double is the type here rather than a corner case",
192    },
193    TargetEntry {
194        tuple: "armv7-linux-gnueabihf",
195        tier: Tier::Recognized,
196        planned: Tier::SupportedWithEvidence,
197        host: Host::No,
198        note: "soft, softfp and hard is an ABI, so it is in the tuple",
199    },
200    TargetEntry {
201        tuple: "armv7-linux-musleabihf",
202        tier: Tier::Recognized,
203        planned: Tier::SupportedWithEvidence,
204        host: Host::No,
205        note: "",
206    },
207    TargetEntry {
208        tuple: "loongarch64-linux-gnu",
209        tier: Tier::Recognized,
210        planned: Tier::SupportedWithEvidence,
211        host: Host::No,
212        note: "LP64D only, and r21 is reserved by the psABI and used by Linux as a percpu base",
213    },
214    TargetEntry {
215        tuple: "powerpc64le-linux-gnu",
216        tier: Tier::Recognized,
217        planned: Tier::SupportedWithEvidence,
218        host: Host::No,
219        note: "ELFv2, with the TOC and the split between local and global entry points",
220    },
221    TargetEntry {
222        tuple: "s390x-linux-gnu",
223        tier: Tier::Recognized,
224        planned: Tier::SupportedWithEvidence,
225        host: Host::No,
226        note: "the big endian row, and the only one with a commercial user base",
227    },
228    TargetEntry {
229        tuple: "aarch64-linux-android",
230        tier: Tier::Recognized,
231        planned: Tier::SupportedWithEvidence,
232        host: Host::No,
233        note: "bionic, API level stub sets, and the packed relocation trap below API 35",
234    },
235    TargetEntry {
236        tuple: "x86_64-linux-android",
237        tier: Tier::Recognized,
238        planned: Tier::InProgress,
239        host: Host::No,
240        note: "the emulator target, which is cheap once aarch64 works",
241    },
242    TargetEntry {
243        tuple: "riscv64-linux-android",
244        tier: Tier::Recognized,
245        planned: Tier::Recognized,
246        host: Host::No,
247        note: "requires an RVA23 baseline, so it is recognized only",
248    },
249    TargetEntry {
250        tuple: "x86_64-linux-gnux32",
251        tier: Tier::Recognized,
252        planned: Tier::Recognized,
253        host: Host::No,
254        note: "ILP32 on a 64-bit ISA, and it exists to prove the data model field is real",
255    },
256    // Darwin.
257    TargetEntry {
258        tuple: "aarch64-macos",
259        tier: Tier::Recognized,
260        planned: Tier::Supported,
261        host: Host::Yes,
262        note: "the four AAPCS64 divergences of the compiler repository's document 12.3",
263    },
264    TargetEntry {
265        tuple: "x86_64-macos",
266        tier: Tier::Recognized,
267        planned: Tier::SupportedWithEvidence,
268        host: Host::Yes,
269        note: "still shipping, and Rosetta makes it how an aarch64 host tests x86-64",
270    },
271    TargetEntry {
272        tuple: "aarch64-ios",
273        tier: Tier::Recognized,
274        planned: Tier::InProgress,
275        host: Host::No,
276        note: "a distinct OS from macOS, with its own platform value and availability macros",
277    },
278    TargetEntry {
279        tuple: "aarch64-ios-simulator",
280        tier: Tier::Recognized,
281        planned: Tier::Recognized,
282        host: Host::No,
283        note: "three table rows rather than work, once the Darwin path exists",
284    },
285    TargetEntry {
286        tuple: "aarch64-ios-macabi",
287        tier: Tier::Recognized,
288        planned: Tier::Recognized,
289        host: Host::No,
290        note: "Mac Catalyst, which zig added in 0.16 and which costs a platform value",
291    },
292    // Windows.
293    TargetEntry {
294        tuple: "x86_64-windows-gnu",
295        tier: Tier::Recognized,
296        planned: Tier::Supported,
297        host: Host::Yes,
298        note: "mingw-w64, and the default env for Windows because it is the one we can ship",
299    },
300    TargetEntry {
301        tuple: "x86_64-windows-msvc",
302        tier: Tier::Recognized,
303        planned: Tier::SupportedWithEvidence,
304        host: Host::Yes,
305        note: "needs a fetched SDK, so the ABI is ours and the dialect is not",
306    },
307    TargetEntry {
308        tuple: "aarch64-windows-gnu",
309        tier: Tier::Recognized,
310        planned: Tier::SupportedWithEvidence,
311        host: Host::Yes,
312        note: "",
313    },
314    TargetEntry {
315        tuple: "aarch64-windows-msvc",
316        tier: Tier::Recognized,
317        planned: Tier::InProgress,
318        host: Host::Yes,
319        note: "",
320    },
321    TargetEntry {
322        tuple: "arm64ec-windows-msvc",
323        tier: Tier::Recognized,
324        planned: Tier::Recognized,
325        host: Host::No,
326        note: "a separate symbol namespace, thunks and hybrid images, declined in document 06.8",
327    },
328    TargetEntry {
329        tuple: "i686-windows-gnu",
330        tier: Tier::Recognized,
331        planned: Tier::InProgress,
332        host: Host::No,
333        note: "stdcall, fastcall and thiscall decoration, the only place C has mangling",
334    },
335    // BSD, illumos, freestanding and wasm.
336    TargetEntry {
337        tuple: "x86_64-freebsd",
338        tier: Tier::Recognized,
339        planned: Tier::SupportedWithEvidence,
340        host: Host::Planned,
341        note: "stub libraries, as zig does, and FreeBSD 14 or later",
342    },
343    TargetEntry {
344        tuple: "aarch64-freebsd",
345        tier: Tier::Recognized,
346        planned: Tier::SupportedWithEvidence,
347        host: Host::Planned,
348        note: "",
349    },
350    TargetEntry {
351        tuple: "x86_64-netbsd",
352        tier: Tier::Recognized,
353        planned: Tier::InProgress,
354        host: Host::No,
355        note: "NetBSD 10.1 or later",
356    },
357    TargetEntry {
358        tuple: "aarch64-netbsd",
359        tier: Tier::Recognized,
360        planned: Tier::InProgress,
361        host: Host::No,
362        note: "",
363    },
364    TargetEntry {
365        tuple: "x86_64-openbsd",
366        tier: Tier::Recognized,
367        planned: Tier::InProgress,
368        host: Host::No,
369        note: "dynamic libc only, and zig took a whole release to add it",
370    },
371    TargetEntry {
372        tuple: "x86_64-illumos",
373        tier: Tier::Recognized,
374        planned: Tier::Recognized,
375        host: Host::No,
376        note: "open, therefore admissible, therefore not excluded on principle",
377    },
378    TargetEntry {
379        tuple: "x86_64-none",
380        tier: Tier::InProgress,
381        planned: Tier::Supported,
382        host: Host::No,
383        note: "the kernel target, which rung 4 needs and needs at tier 1",
384    },
385    TargetEntry {
386        tuple: "aarch64-none",
387        tier: Tier::InProgress,
388        planned: Tier::Supported,
389        host: Host::No,
390        note: "",
391    },
392    TargetEntry {
393        tuple: "riscv64-none",
394        tier: Tier::InProgress,
395        planned: Tier::Supported,
396        host: Host::No,
397        note: "",
398    },
399    TargetEntry {
400        tuple: "i686-none",
401        tier: Tier::InProgress,
402        planned: Tier::SupportedWithEvidence,
403        host: Host::No,
404        note: "capped at its architecture's tier, per the note at the top of this module",
405    },
406    TargetEntry {
407        tuple: "armv7-none-eabi",
408        tier: Tier::InProgress,
409        planned: Tier::SupportedWithEvidence,
410        host: Host::No,
411        note: "soft float, because a bare metal core may have no FPU",
412    },
413    TargetEntry {
414        tuple: "armv7m-none-eabi",
415        tier: Tier::InProgress,
416        planned: Tier::SupportedWithEvidence,
417        host: Host::No,
418        note: "Thumb only, and the smallest target in the table",
419    },
420    TargetEntry {
421        tuple: "wasm32-wasip1",
422        tier: Tier::Recognized,
423        planned: Tier::SupportedWithEvidence,
424        host: Host::No,
425        note: "one linear memory, no signals, and structured control flow",
426    },
427    TargetEntry {
428        tuple: "wasm32-wasip3",
429        tier: Tier::Recognized,
430        planned: Tier::InProgress,
431        host: Host::No,
432        note: "a different ABI from p1 and p2, with the stack pointer and TLS base in a context",
433    },
434    TargetEntry {
435        tuple: "wasm32-none",
436        tier: Tier::Recognized,
437        planned: Tier::SupportedWithEvidence,
438        host: Host::No,
439        note: "freestanding wasm, which is the second back end without the libc question",
440    },
441];
442
443/// Find the table row for a target, by canonical spelling.
444///
445/// Takes a parsed tuple rather than a string so that `x86_64-unknown-linux-gnu` and
446/// `x86_64-linux-gnu` find the same row. That is the reason canonicalization exists.
447pub fn lookup(target: &TargetTuple) -> Option<&'static TargetEntry> {
448    let canonical = target.to_canonical_string();
449    TARGETS.iter().find(|entry| entry.tuple == canonical)
450}
451
452/// How many rows are at each tier today, and how many the plan commits to.
453///
454/// Returned rather than written down, because `spec/cross-compile/04-target-matrix.md` section 4.7 forbids a
455/// count that a human maintains next to a table that changes.
456pub fn counts_by_planned_tier() -> [usize; 4] {
457    let mut counts = [0usize; 4];
458    for entry in TARGETS {
459        counts[usize::from(entry.planned.number()) - 1] += 1;
460    }
461    counts
462}
463
464/// How many rows the plan commits to compiling and linking, which is the number claim 1 is
465/// measured against.
466pub fn planned_working_count() -> usize {
467    TARGETS.iter().filter(|entry| entry.planned.compiles_and_links()).count()
468}