Skip to main content

lamina_platform/
arch_table.rs

1//! The architecture table: every property of every architecture, stated once.
2//!
3//! One block per architecture, generated by [`arch_table!`] into the
4//! [`TargetArchitecture`] enum, [`TargetArchitecture::spec`], [`TargetArchitecture::all`],
5//! the `Display` spelling and the `FromStr` spellings. Nothing about an architecture is
6//! stated anywhere else, so adding one is one block and adding a property is one field.
7//!
8//! The compiler enforces both halves of that. A block missing a field does not match the
9//! macro's entry rule, so an architecture cannot exist without every property; the
10//! generated `spec` match has no wildcard arm; and `all` and the target list are derived
11//! from the same table, so they cannot fall behind it.
12
13use crate::arch_variant::{
14    Aarch64Level, ArchVariant, Armv7Variant, PowerLevel, RiscvIsa, VariantAxis,
15};
16use crate::cpu_level::X86CpuLevel;
17use crate::environment::TargetEnvironment;
18
19/// Width of a data pointer. Pointer size and alignment derive from it.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub enum PointerWidth {
22    Bits32,
23    Bits64,
24    Bits128,
25}
26
27impl PointerWidth {
28    pub const fn bytes(self) -> usize {
29        match self {
30            Self::Bits32 => 4,
31            Self::Bits64 => 8,
32            Self::Bits128 => 16,
33        }
34    }
35}
36
37/// Byte order of multi-byte values in memory.
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
39pub enum Endianness {
40    Little,
41    Big,
42}
43
44/// What a finished compilation of this target is.
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
46pub enum OutputArtifact {
47    /// Object files that a linker turns into an executable.
48    Executable,
49    /// A self-contained module, assembled but never linked.
50    Module,
51    /// Assembly text handed on as-is.
52    Text,
53}
54
55/// Size and alignment, in bytes, of the `va_list` an architecture's C ABI defines.
56///
57/// A `va_list` is a struct the ABI names field by field, so its size is a property of
58/// the architecture and not of the pointer width. The shapes stated in the table are:
59///
60/// - x86_64: System V AMD64 ABI §3.5.7, `struct { unsigned int gp_offset, fp_offset;
61///   void *overflow_arg_area, *reg_save_area; }` — 24 bytes.
62/// - aarch64: AAPCS64 (IHI 0055) appendix "Variable Argument Lists", `struct { void
63///   *__stack, *__gr_top, *__vr_top; int __gr_offs, __vr_offs; }` — 32 bytes.
64/// - s390x: zSeries ELF ABI supplement, `struct { long __gpr, __fpr; void
65///   *__overflow_arg_area, *__reg_save_area; }` — 32 bytes.
66/// - riscv, powerpc64 ELFv2, loongarch, mips n64, wasm: a plain `void *`, one pointer.
67/// - i686: Intel386 psABI §3.17, `char *` — 4 bytes.
68/// - 32-bit ARM: AAPCS (IHI 0042) §7.1, `struct __va_list { void *__ap; }` — 4 bytes.
69///
70/// [`VaListAbi::Undefined`] is a statement that no ABI defines one, not an omission.
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
72pub enum VaListAbi {
73    Defined { size: u64, alignment: u64 },
74    Undefined,
75}
76
77impl VaListAbi {
78    pub const fn of(size: u64, alignment: u64) -> Self {
79        Self::Defined { size, alignment }
80    }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
84pub enum FloatAggregateAbi {
85    Refused,
86    SysV,
87    Aapcs64,
88    Win64,
89    /// RISC-V hard-float: a struct that flattens to one or two floating-point members,
90    /// or to one floating-point and one integer member, takes a register per member.
91    Riscv64Lp64d,
92    /// z/Architecture ELF: a record whose one member is a float travels as that float;
93    /// every other aggregate takes a general register or travels by reference.
94    S390x,
95}
96
97/// The facts a target-independent lowering needs to turn IR into MIR.
98#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
99pub struct TargetDataLayout {
100    pub pointer_width: PointerWidth,
101    pub int_register_bits: u32,
102    pub endianness: Endianness,
103    pub va_list: VaListAbi,
104    pub float_aggregates: FloatAggregateAbi,
105}
106
107impl TargetDataLayout {
108    pub const fn has_64bit_int_registers(self) -> bool {
109        self.int_register_bits >= 64
110    }
111}
112
113/// The ELF interpreter an architecture takes, one path per environment it supports.
114///
115/// `None` is a statement that the combination has no interpreter, not an omission.
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
117pub struct LinuxInterpreters {
118    /// glibc, with the architecture's own floating-point ABI.
119    pub gnu: Option<&'static str>,
120    /// glibc, with the soft-float ABI. Reached by `eabi` and by a RISC-V ISA without `d`.
121    pub gnu_soft_float: Option<&'static str>,
122    /// musl libc.
123    pub musl: Option<&'static str>,
124    /// Lamina's own C library. `None` on every architecture today: no Lamina libc has
125    /// been written, so there is no loader to name. A port fills this in here.
126    pub lamina: Option<&'static str>,
127}
128
129/// Cross-toolchain triples, by C library.
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
131pub struct ToolchainTriples {
132    pub gnu: Option<&'static str>,
133    pub musl: Option<&'static str>,
134    /// Lamina's own toolchain. `None` on every architecture today: nothing outside this
135    /// repository supplies a Lamina-libc cross compiler, and nothing should be guessed.
136    pub lamina: Option<&'static str>,
137}
138
139/// Everything an architecture states about itself.
140///
141/// Read it through [`crate::Target::spec`], which applies the variant's refinements;
142/// [`TargetArchitecture::spec`] is the unrefined form the table states.
143#[derive(Debug, Clone, Copy, PartialEq, Eq)]
144pub struct ArchSpec {
145    /// Canonical spelling, the one `Display` writes.
146    pub token: &'static str,
147    /// Further spellings `FromStr` accepts for this architecture.
148    pub spellings: &'static [&'static str],
149    /// Target strings this architecture contributes to the supported-target list.
150    pub targets: &'static [&'static str],
151    pub pointer_width: PointerWidth,
152    /// Widest integer one register holds. Not the pointer width: wasm32 has 32-bit
153    /// pointers and 64-bit integer registers.
154    pub max_int_register_bits: u32,
155    pub endianness: Endianness,
156    /// Size and alignment of this architecture's C `va_list`.
157    pub va_list: VaListAbi,
158    /// Alignment the stack pointer holds at a call. `1` means there is no machine stack.
159    pub stack_alignment_at_call: usize,
160    pub stack_page_size: usize,
161    /// Whether an argument with 16-byte alignment starts at its own alignment rather than
162    /// at the next word. AAPCS64 rounds the integer register counter up to even before
163    /// such an argument; the riscv psABI makes that rule variadic-only and ppc64 does not
164    /// round for named arguments either.
165    pub honors_argument_alignment: bool,
166    pub float_aggregates: FloatAggregateAbi,
167    pub output_artifact: OutputArtifact,
168    /// Environment a hosted target of this architecture means when the target string
169    /// names none.
170    pub hosted_environment: TargetEnvironment,
171    /// Environments a target string may name for this architecture, beside
172    /// [`TargetEnvironment::Freestanding`], which every architecture accepts.
173    pub environments: &'static [TargetEnvironment],
174    pub interpreters: LinuxInterpreters,
175    pub toolchain: ToolchainTriples,
176    /// Directory name under the Windows SDK `Lib` tree. `None` means Windows has no
177    /// library layout for this architecture, and asking for one is an error rather than
178    /// a search of the x86_64 directories.
179    pub msvc_library_arch: Option<&'static str>,
180    /// The sub-architecture axis this architecture has, and its default.
181    pub variants: VariantAxis,
182}
183
184impl ArchSpec {
185    pub const fn data_layout(&self) -> TargetDataLayout {
186        TargetDataLayout {
187            pointer_width: self.pointer_width,
188            int_register_bits: self.max_int_register_bits,
189            endianness: self.endianness,
190            va_list: self.va_list,
191            float_aggregates: self.float_aggregates,
192        }
193    }
194
195    pub const fn has_64bit_int_registers(&self) -> bool {
196        self.max_int_register_bits >= 64
197    }
198
199    /// The ELF interpreter for an environment, or `None` when there is none.
200    pub const fn interpreter(&self, environment: TargetEnvironment) -> Option<&'static str> {
201        match environment {
202            TargetEnvironment::Gnu | TargetEnvironment::Eabihf => self.interpreters.gnu,
203            TargetEnvironment::Eabi => self.interpreters.gnu_soft_float,
204            TargetEnvironment::Musl => self.interpreters.musl,
205            TargetEnvironment::Lamina => self.interpreters.lamina,
206            TargetEnvironment::Freestanding => None,
207        }
208    }
209
210    /// The cross-toolchain triple for an environment.
211    pub const fn toolchain_triple(&self, environment: TargetEnvironment) -> Option<&'static str> {
212        match environment {
213            TargetEnvironment::Gnu | TargetEnvironment::Eabi | TargetEnvironment::Eabihf => {
214                self.toolchain.gnu
215            }
216            TargetEnvironment::Musl => self.toolchain.musl,
217            TargetEnvironment::Lamina => self.toolchain.lamina,
218            TargetEnvironment::Freestanding => self.toolchain.gnu,
219        }
220    }
221
222    /// Whether a target string may name this environment for this architecture.
223    pub fn accepts_environment(&self, environment: TargetEnvironment) -> bool {
224        environment == TargetEnvironment::Freestanding || self.environments.contains(&environment)
225    }
226}
227
228macro_rules! arch_table {
229    ($(
230        $(#[doc = $doc:literal])*
231        $(#[cfg($cfg:meta)])?
232        $variant:ident {
233            token: $token:literal,
234            spellings: [$($spelling:literal),* $(,)?],
235            targets: [$($target:literal),* $(,)?],
236            pointer_width: $pointer_width:ident,
237            max_int_register_bits: $int_bits:literal,
238            endianness: $endianness:ident,
239            va_list: $va_list:expr,
240            stack_alignment_at_call: $stack_align:literal,
241            stack_page_size: $page_size:literal,
242            honors_argument_alignment: $honors:literal,
243            float_aggregates: $float_aggregates:ident,
244            output_artifact: $artifact:ident,
245            hosted_environment: $hosted:ident,
246            environments: [$($environment:ident),* $(,)?],
247            interpreters: {
248                gnu: $i_gnu:expr,
249                gnu_soft_float: $i_soft:expr,
250                musl: $i_musl:expr,
251                lamina: $i_lamina:expr
252            },
253            toolchain: { gnu: $t_gnu:expr, musl: $t_musl:expr, lamina: $t_lamina:expr },
254            msvc_library_arch: $msvc:expr,
255            variants: $axis:expr,
256        }
257    )*) => {
258        /// An architecture lamina has a backend for, or has reserved a name for.
259        ///
260        /// Every property is stated in the table in [`crate::arch_table`]; there are no
261        /// per-property matches anywhere else.
262        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
263        pub enum TargetArchitecture {
264            $(
265                $(#[doc = $doc])*
266                $(#[cfg($cfg)])?
267                $variant,
268            )*
269        }
270
271        impl TargetArchitecture {
272            /// Everything this architecture states about itself.
273            pub const fn spec(self) -> ArchSpec {
274                match self {
275                    $(
276                        $(#[cfg($cfg)])?
277                        Self::$variant => ArchSpec {
278                            token: $token,
279                            spellings: &[$($spelling),*],
280                            targets: &[$($target),*],
281                            pointer_width: PointerWidth::$pointer_width,
282                            max_int_register_bits: $int_bits,
283                            endianness: Endianness::$endianness,
284                            va_list: $va_list,
285                            stack_alignment_at_call: $stack_align,
286                            stack_page_size: $page_size,
287                            honors_argument_alignment: $honors,
288                            float_aggregates: FloatAggregateAbi::$float_aggregates,
289                            output_artifact: OutputArtifact::$artifact,
290                            hosted_environment: TargetEnvironment::$hosted,
291                            environments: &[$(TargetEnvironment::$environment),*],
292                            interpreters: LinuxInterpreters {
293                                gnu: $i_gnu,
294                                gnu_soft_float: $i_soft,
295                                musl: $i_musl,
296                                lamina: $i_lamina,
297                            },
298                            toolchain: ToolchainTriples {
299                                gnu: $t_gnu,
300                                musl: $t_musl,
301                                lamina: $t_lamina,
302                            },
303                            msvc_library_arch: $msvc,
304                            variants: $axis,
305                        },
306                    )*
307                }
308            }
309
310            /// Every architecture in the table, in declaration order.
311            pub fn all() -> Vec<Self> {
312                let mut architectures = Vec::new();
313                $(
314                    $(#[cfg($cfg)])?
315                    architectures.push(Self::$variant);
316                )*
317                architectures
318            }
319
320            /// The architecture a bare spelling names, without consulting the refusals.
321            pub(crate) fn from_spelling(spelling: &str) -> Option<Self> {
322                $(
323                    $(#[cfg($cfg)])?
324                    if spelling == $token $(|| spelling == $spelling)* {
325                        return Some(Self::$variant);
326                    }
327                )*
328                None
329            }
330        }
331    };
332}
333
334arch_table! {
335    X86_64 {
336        token: "x86_64",
337        spellings: [],
338        targets: [
339            "x86_64_unknown", "x86_64_linux", "x86_64_windows", "x86_64_macos",
340            "x86_64_android", "x86_64_ios", "x86_64_tvos", "x86_64_watchos",
341            "x86_64_freebsd", "x86_64_orbis", "x86_64_prospero",
342        ],
343        pointer_width: Bits64,
344        max_int_register_bits: 64,
345        endianness: Little,
346        va_list: VaListAbi::of(24, 8),
347        stack_alignment_at_call: 16,
348        stack_page_size: 4096,
349        honors_argument_alignment: true,
350        float_aggregates: SysV,
351        output_artifact: Executable,
352        hosted_environment: Gnu,
353        environments: [Gnu, Musl, Lamina],
354        interpreters: {
355            gnu: Some("/lib64/ld-linux-x86-64.so.2"),
356            gnu_soft_float: None,
357            musl: Some("/lib/ld-musl-x86_64.so.1"),
358            lamina: None
359        },
360        toolchain: { gnu: Some("x86_64-linux-gnu"), musl: Some("x86_64-linux-musl"), lamina: None },
361        msvc_library_arch: Some("x64"),
362        variants: VariantAxis::X86(X86CpuLevel::V2),
363    }
364
365    Aarch64 {
366        token: "aarch64",
367        spellings: [],
368        targets: [
369            "aarch64_unknown", "aarch64_macos", "aarch64_linux", "aarch64_windows",
370            "aarch64_android", "aarch64_ios", "aarch64_tvos", "aarch64_watchos",
371            "aarch64_visionos", "aarch64_horizon",
372        ],
373        pointer_width: Bits64,
374        max_int_register_bits: 64,
375        endianness: Little,
376        va_list: VaListAbi::of(32, 8),
377        stack_alignment_at_call: 16,
378        stack_page_size: 4096,
379        honors_argument_alignment: true,
380        float_aggregates: Aapcs64,
381        output_artifact: Executable,
382        hosted_environment: Gnu,
383        environments: [Gnu, Musl, Lamina],
384        interpreters: {
385            gnu: Some("/lib/ld-linux-aarch64.so.1"),
386            gnu_soft_float: None,
387            musl: Some("/lib/ld-musl-aarch64.so.1"),
388            lamina: None
389        },
390        toolchain: { gnu: Some("aarch64-linux-gnu"), musl: Some("aarch64-linux-musl"), lamina: None },
391        msvc_library_arch: Some("arm64"),
392        variants: VariantAxis::Aarch64(Aarch64Level::V8_0),
393    }
394
395    #[doc = "ARX64, an experimental architecture with no silicon."]
396    #[cfg(feature = "nightly")]
397    Arx64 {
398        token: "arx64",
399        spellings: [],
400        targets: ["arx64_unknown"],
401        pointer_width: Bits64,
402        max_int_register_bits: 64,
403        endianness: Little,
404        va_list: VaListAbi::Undefined,
405        stack_alignment_at_call: 16,
406        stack_page_size: 4096,
407        honors_argument_alignment: false,
408        float_aggregates: Refused,
409        output_artifact: Executable,
410        hosted_environment: Freestanding,
411        environments: [],
412        interpreters: { gnu: None, gnu_soft_float: None, musl: None, lamina: None },
413        toolchain: { gnu: None, musl: None, lamina: None },
414        msvc_library_arch: None,
415        variants: VariantAxis::None,
416    }
417
418    #[doc = "32-bit ARM with no revision, float ABI or profile named. Reserved, and"]
419    #[doc = "refused by `FromStr` in favour of `armv7`, `thumbv6m` and `thumbv7m`."]
420    Arm32 {
421        token: "arm32",
422        spellings: [],
423        targets: [],
424        pointer_width: Bits32,
425        max_int_register_bits: 32,
426        endianness: Little,
427        va_list: VaListAbi::of(4, 4),
428        stack_alignment_at_call: 8,
429        stack_page_size: 4096,
430        honors_argument_alignment: false,
431        float_aggregates: Refused,
432        output_artifact: Executable,
433        hosted_environment: Eabi,
434        environments: [Eabi, Eabihf],
435        interpreters: { gnu: None, gnu_soft_float: None, musl: None, lamina: None },
436        toolchain: { gnu: None, musl: None, lamina: None },
437        msvc_library_arch: Some("arm"),
438        variants: VariantAxis::None,
439    }
440
441    Armv7 {
442        token: "armv7",
443        spellings: ["armv7a", "armv7hf"],
444        targets: ["armv7_linux"],
445        pointer_width: Bits32,
446        max_int_register_bits: 32,
447        endianness: Little,
448        va_list: VaListAbi::of(4, 4),
449        stack_alignment_at_call: 16,
450        stack_page_size: 4096,
451        honors_argument_alignment: false,
452        float_aggregates: Refused,
453        output_artifact: Executable,
454        hosted_environment: Eabihf,
455        environments: [Eabi, Eabihf],
456        interpreters: {
457            gnu: Some("/lib/ld-linux-armhf.so.3"),
458            gnu_soft_float: Some("/lib/ld-linux.so.3"),
459            musl: None,
460            lamina: None
461        },
462        toolchain: { gnu: Some("arm-linux-gnueabihf"), musl: None, lamina: None },
463        msvc_library_arch: Some("arm"),
464        variants: VariantAxis::Armv7(Armv7Variant::cortex_a7()),
465    }
466
467    #[doc = "ARMv6-M, Thumb-1 only: Cortex-M0 and M0+."]
468    Thumbv6m {
469        token: "thumbv6m",
470        spellings: ["armv6m", "cortex-m0"],
471        targets: ["thumbv6m_unknown", "thumbv6m_linux"],
472        pointer_width: Bits32,
473        max_int_register_bits: 32,
474        endianness: Little,
475        va_list: VaListAbi::of(4, 4),
476        stack_alignment_at_call: 8,
477        stack_page_size: 4096,
478        honors_argument_alignment: false,
479        float_aggregates: Refused,
480        output_artifact: Executable,
481        hosted_environment: Eabi,
482        environments: [Eabi],
483        interpreters: { gnu: None, gnu_soft_float: None, musl: None, lamina: None },
484        toolchain: { gnu: None, musl: None, lamina: None },
485        msvc_library_arch: Some("arm"),
486        variants: VariantAxis::None,
487    }
488
489    #[doc = "ARMv7-M, Thumb-2: Cortex-M3, M4 and M7."]
490    Thumbv7m {
491        token: "thumbv7m",
492        spellings: ["armv7m", "cortex-m3"],
493        targets: ["thumbv7m_unknown", "thumbv7m_linux"],
494        pointer_width: Bits32,
495        max_int_register_bits: 32,
496        endianness: Little,
497        va_list: VaListAbi::of(4, 4),
498        stack_alignment_at_call: 8,
499        stack_page_size: 4096,
500        honors_argument_alignment: false,
501        float_aggregates: Refused,
502        output_artifact: Executable,
503        hosted_environment: Eabi,
504        environments: [Eabi],
505        interpreters: { gnu: None, gnu_soft_float: None, musl: None, lamina: None },
506        toolchain: { gnu: None, musl: None, lamina: None },
507        msvc_library_arch: Some("arm"),
508        variants: VariantAxis::Armv7(Armv7Variant::cortex_m3()),
509    }
510
511    I686 {
512        token: "i686",
513        spellings: ["i386", "x86"],
514        targets: ["i686_linux"],
515        pointer_width: Bits32,
516        max_int_register_bits: 32,
517        endianness: Little,
518        va_list: VaListAbi::of(4, 4),
519        stack_alignment_at_call: 16,
520        stack_page_size: 4096,
521        honors_argument_alignment: false,
522        float_aggregates: Refused,
523        output_artifact: Executable,
524        hosted_environment: Gnu,
525        environments: [Gnu, Musl],
526        interpreters: {
527            gnu: Some("/lib/ld-linux.so.2"),
528            gnu_soft_float: None,
529            musl: Some("/lib/ld-musl-i386.so.1"),
530            lamina: None
531        },
532        toolchain: { gnu: Some("i686-linux-gnu"), musl: Some("i686-linux-musl"), lamina: None },
533        msvc_library_arch: Some("x86"),
534        variants: VariantAxis::None,
535    }
536
537    Riscv32 {
538        token: "riscv32",
539        spellings: [],
540        targets: ["riscv32_unknown", "riscv32_linux"],
541        pointer_width: Bits32,
542        max_int_register_bits: 32,
543        endianness: Little,
544        va_list: VaListAbi::of(4, 4),
545        stack_alignment_at_call: 16,
546        stack_page_size: 4096,
547        honors_argument_alignment: false,
548        float_aggregates: Refused,
549        output_artifact: Executable,
550        hosted_environment: Gnu,
551        environments: [Gnu, Musl],
552        interpreters: {
553            gnu: Some("/lib/ld-linux-riscv32-ilp32d.so.1"),
554            gnu_soft_float: Some("/lib/ld-linux-riscv32-ilp32.so.1"),
555            musl: Some("/lib/ld-musl-riscv32.so.1"),
556            lamina: None
557        },
558        toolchain: { gnu: Some("riscv32-linux-gnu"), musl: Some("riscv32-linux-musl"), lamina: None },
559        msvc_library_arch: None,
560        variants: VariantAxis::Riscv(RiscvIsa::general(32)),
561    }
562
563    Riscv64 {
564        token: "riscv64",
565        spellings: ["riscv64gc"],
566        targets: ["riscv64_linux", "riscv64_unknown", "riscv64gc_linux"],
567        pointer_width: Bits64,
568        max_int_register_bits: 64,
569        endianness: Little,
570        va_list: VaListAbi::of(8, 8),
571        stack_alignment_at_call: 16,
572        stack_page_size: 4096,
573        honors_argument_alignment: false,
574        float_aggregates: Riscv64Lp64d,
575        output_artifact: Executable,
576        hosted_environment: Gnu,
577        environments: [Gnu, Musl],
578        interpreters: {
579            gnu: Some("/lib/ld-linux-riscv64-lp64d.so.1"),
580            gnu_soft_float: Some("/lib/ld-linux-riscv64-lp64.so.1"),
581            musl: Some("/lib/ld-musl-riscv64.so.1"),
582            lamina: None
583        },
584        toolchain: { gnu: Some("riscv64-linux-gnu"), musl: Some("riscv64-linux-musl"), lamina: None },
585        msvc_library_arch: None,
586        variants: VariantAxis::Riscv(RiscvIsa::general(64)),
587    }
588
589    #[cfg(feature = "nightly")]
590    Riscv128 {
591        token: "riscv128",
592        spellings: [],
593        targets: ["riscv128_unknown"],
594        pointer_width: Bits128,
595        max_int_register_bits: 128,
596        endianness: Little,
597        va_list: VaListAbi::Undefined,
598        stack_alignment_at_call: 16,
599        stack_page_size: 4096,
600        honors_argument_alignment: false,
601        float_aggregates: Refused,
602        output_artifact: Executable,
603        hosted_environment: Gnu,
604        environments: [Gnu],
605        interpreters: { gnu: None, gnu_soft_float: None, musl: None, lamina: None },
606        toolchain: { gnu: None, musl: None, lamina: None },
607        msvc_library_arch: None,
608        variants: VariantAxis::Riscv(RiscvIsa::general(128)),
609    }
610
611    Wasm32 {
612        token: "wasm32",
613        spellings: ["wasm"],
614        targets: ["wasm32_unknown", "wasm32_wasi"],
615        pointer_width: Bits32,
616        max_int_register_bits: 64,
617        endianness: Little,
618        va_list: VaListAbi::of(4, 4),
619        stack_alignment_at_call: 1,
620        stack_page_size: 4096,
621        honors_argument_alignment: false,
622        float_aggregates: Refused,
623        output_artifact: Module,
624        hosted_environment: Freestanding,
625        environments: [],
626        interpreters: { gnu: None, gnu_soft_float: None, musl: None, lamina: None },
627        toolchain: { gnu: None, musl: None, lamina: None },
628        msvc_library_arch: None,
629        variants: VariantAxis::None,
630    }
631
632    Wasm64 {
633        token: "wasm64",
634        spellings: [],
635        targets: ["wasm64_unknown"],
636        pointer_width: Bits64,
637        max_int_register_bits: 64,
638        endianness: Little,
639        va_list: VaListAbi::of(8, 8),
640        stack_alignment_at_call: 1,
641        stack_page_size: 4096,
642        honors_argument_alignment: false,
643        float_aggregates: Refused,
644        output_artifact: Module,
645        hosted_environment: Freestanding,
646        environments: [],
647        interpreters: { gnu: None, gnu_soft_float: None, musl: None, lamina: None },
648        toolchain: { gnu: None, musl: None, lamina: None },
649        msvc_library_arch: None,
650        variants: VariantAxis::None,
651    }
652
653    #[doc = "Little-endian 64-bit POWER, ELFv2."]
654    PowerPC64 {
655        token: "ppc64le",
656        spellings: [],
657        targets: ["ppc64le_linux", "ppc64le_unknown"],
658        pointer_width: Bits64,
659        max_int_register_bits: 64,
660        endianness: Little,
661        va_list: VaListAbi::of(8, 8),
662        stack_alignment_at_call: 16,
663        stack_page_size: 4096,
664        honors_argument_alignment: false,
665        float_aggregates: Refused,
666        output_artifact: Executable,
667        hosted_environment: Gnu,
668        environments: [Gnu, Musl],
669        interpreters: {
670            gnu: Some("/lib64/ld64.so.2"),
671            gnu_soft_float: None,
672            musl: Some("/lib/ld-musl-powerpc64le.so.1"),
673            lamina: None
674        },
675        toolchain: { gnu: Some("powerpc64le-linux-gnu"), musl: Some("powerpc64le-linux-musl"), lamina: None },
676        msvc_library_arch: None,
677        variants: VariantAxis::Power(PowerLevel::Power8),
678    }
679
680    #[doc = "Big-endian 64-bit POWER, ELFv2."]
681    PowerPC64Be {
682        token: "ppc64",
683        spellings: ["powerpc64"],
684        targets: ["ppc64_linux"],
685        pointer_width: Bits64,
686        max_int_register_bits: 64,
687        endianness: Big,
688        va_list: VaListAbi::of(8, 8),
689        stack_alignment_at_call: 16,
690        stack_page_size: 4096,
691        honors_argument_alignment: false,
692        float_aggregates: Refused,
693        output_artifact: Executable,
694        hosted_environment: Gnu,
695        environments: [Gnu, Musl],
696        interpreters: {
697            gnu: Some("/lib64/ld64.so.2"),
698            gnu_soft_float: None,
699            musl: Some("/lib/ld-musl-powerpc64.so.1"),
700            lamina: None
701        },
702        toolchain: { gnu: Some("powerpc64-linux-gnu"), musl: Some("powerpc64-linux-musl"), lamina: None },
703        msvc_library_arch: None,
704        variants: VariantAxis::Power(PowerLevel::Power8),
705    }
706
707    Mips64 {
708        token: "mips64",
709        spellings: [],
710        targets: ["mips64_linux"],
711        pointer_width: Bits64,
712        max_int_register_bits: 64,
713        endianness: Big,
714        va_list: VaListAbi::of(8, 8),
715        stack_alignment_at_call: 16,
716        stack_page_size: 4096,
717        honors_argument_alignment: false,
718        float_aggregates: Refused,
719        output_artifact: Executable,
720        hosted_environment: Gnu,
721        environments: [Gnu, Musl],
722        interpreters: {
723            gnu: Some("/lib64/ld.so.1"),
724            gnu_soft_float: None,
725            musl: Some("/lib/ld-musl-mips64.so.1"),
726            lamina: None
727        },
728        toolchain: { gnu: Some("mips64-linux-gnuabi64"), musl: Some("mips64-linux-musl"), lamina: None },
729        msvc_library_arch: None,
730        variants: VariantAxis::None,
731    }
732
733    Mips64El {
734        token: "mips64el",
735        spellings: ["mips64le"],
736        targets: ["mips64el_linux"],
737        pointer_width: Bits64,
738        max_int_register_bits: 64,
739        endianness: Little,
740        va_list: VaListAbi::of(8, 8),
741        stack_alignment_at_call: 16,
742        stack_page_size: 4096,
743        honors_argument_alignment: false,
744        float_aggregates: Refused,
745        output_artifact: Executable,
746        hosted_environment: Gnu,
747        environments: [Gnu, Musl],
748        interpreters: {
749            gnu: Some("/lib64/ld.so.1"),
750            gnu_soft_float: None,
751            musl: Some("/lib/ld-musl-mips64el.so.1"),
752            lamina: None
753        },
754        toolchain: { gnu: Some("mips64el-linux-gnuabi64"), musl: Some("mips64el-linux-musl"), lamina: None },
755        msvc_library_arch: None,
756        variants: VariantAxis::None,
757    }
758
759    LoongArch64 {
760        token: "loongarch64",
761        spellings: ["loong64"],
762        targets: ["loongarch64_linux"],
763        pointer_width: Bits64,
764        max_int_register_bits: 64,
765        endianness: Little,
766        va_list: VaListAbi::of(8, 8),
767        stack_alignment_at_call: 16,
768        stack_page_size: 4096,
769        honors_argument_alignment: false,
770        float_aggregates: Refused,
771        output_artifact: Executable,
772        hosted_environment: Gnu,
773        environments: [Gnu, Musl],
774        interpreters: {
775            gnu: Some("/lib64/ld-linux-loongarch-lp64d.so.1"),
776            gnu_soft_float: None,
777            musl: Some("/lib/ld-musl-loongarch64.so.1"),
778            lamina: None
779        },
780        toolchain: { gnu: Some("loongarch64-linux-gnu"), musl: Some("loongarch64-linux-musl"), lamina: None },
781        msvc_library_arch: None,
782        variants: VariantAxis::None,
783    }
784
785    S390x {
786        token: "s390x",
787        spellings: ["systemz"],
788        targets: ["s390x_linux"],
789        pointer_width: Bits64,
790        max_int_register_bits: 64,
791        endianness: Big,
792        va_list: VaListAbi::of(32, 8),
793        stack_alignment_at_call: 8,
794        stack_page_size: 4096,
795        honors_argument_alignment: false,
796        float_aggregates: S390x,
797        output_artifact: Executable,
798        hosted_environment: Gnu,
799        environments: [Gnu, Musl],
800        interpreters: {
801            gnu: Some("/lib/ld64.so.1"),
802            gnu_soft_float: None,
803            musl: Some("/lib/ld-musl-s390x.so.1"),
804            lamina: None
805        },
806        toolchain: { gnu: Some("s390x-linux-gnu"), musl: Some("s390x-linux-musl"), lamina: None },
807        msvc_library_arch: None,
808        variants: VariantAxis::None,
809    }
810
811    Nvptx64 {
812        token: "nvptx64",
813        spellings: [],
814        targets: ["nvptx64_cuda"],
815        pointer_width: Bits64,
816        max_int_register_bits: 64,
817        endianness: Little,
818        va_list: VaListAbi::Undefined,
819        stack_alignment_at_call: 1,
820        stack_page_size: 4096,
821        honors_argument_alignment: false,
822        float_aggregates: Refused,
823        output_artifact: Text,
824        hosted_environment: Freestanding,
825        environments: [],
826        interpreters: { gnu: None, gnu_soft_float: None, musl: None, lamina: None },
827        toolchain: { gnu: None, musl: None, lamina: None },
828        msvc_library_arch: None,
829        variants: VariantAxis::None,
830    }
831
832    Amdgcn {
833        token: "amdgcn",
834        spellings: [],
835        targets: ["amdgcn_amdhsa"],
836        pointer_width: Bits64,
837        max_int_register_bits: 64,
838        endianness: Little,
839        va_list: VaListAbi::Undefined,
840        stack_alignment_at_call: 1,
841        stack_page_size: 4096,
842        honors_argument_alignment: false,
843        float_aggregates: Refused,
844        output_artifact: Text,
845        hosted_environment: Freestanding,
846        environments: [],
847        interpreters: { gnu: None, gnu_soft_float: None, musl: None, lamina: None },
848        toolchain: { gnu: None, musl: None, lamina: None },
849        msvc_library_arch: None,
850        variants: VariantAxis::None,
851    }
852
853    #[cfg(feature = "nightly")]
854    Lisa {
855        token: "lisa",
856        spellings: [],
857        targets: [],
858        pointer_width: Bits64,
859        max_int_register_bits: 64,
860        endianness: Little,
861        va_list: VaListAbi::Undefined,
862        stack_alignment_at_call: 16,
863        stack_page_size: 4096,
864        honors_argument_alignment: false,
865        float_aggregates: Refused,
866        output_artifact: Executable,
867        hosted_environment: Freestanding,
868        environments: [],
869        interpreters: { gnu: None, gnu_soft_float: None, musl: None, lamina: None },
870        toolchain: { gnu: None, musl: None, lamina: None },
871        msvc_library_arch: None,
872        variants: VariantAxis::None,
873    }
874}
875
876/// Spellings that name a real machine lamina has decided not to implement, each with the
877/// reason and the spelling to use instead. Consulted before the architecture spellings,
878/// so a refused name never resolves to an architecture.
879pub(crate) const REFUSED_SPELLINGS: &[(&str, &str)] = &[
880    (
881        "arm32",
882        "Architecture 'arm32' is not implemented\n  Hint: 'arm32' names no ARM revision, floating-point ABI or profile, so there is no machine to generate code for. The implemented 32-bit ARM target is armv7, AAPCS with VFP hard float: write 'armv7' (e.g. armv7_linux), or 'thumbv6m'/'thumbv7m' for Cortex-M",
883    ),
884    (
885        "ppc64-elfv1",
886        "Architecture 'ppc64-elfv1' is not supported\n  Hint: big-endian POWER here is ELFv2. ELFv1 calls through .opd function descriptors and is a different ABI. Write 'ppc64' (e.g. ppc64_linux)",
887    ),
888    (
889        "powerpc64-elfv1",
890        "Architecture 'ppc64-elfv1' is not supported\n  Hint: big-endian POWER here is ELFv2. ELFv1 calls through .opd function descriptors and is a different ABI. Write 'ppc64' (e.g. ppc64_linux)",
891    ),
892    (
893        "mips",
894        "Architecture 'mips' is not supported\n  Hint: lamina implements the N64 ABI on 64-bit MIPS only, not O32. Write 'mips64' or 'mips64el' (e.g. mips64_linux)",
895    ),
896    (
897        "mipsel",
898        "Architecture 'mips' is not supported\n  Hint: lamina implements the N64 ABI on 64-bit MIPS only, not O32. Write 'mips64' or 'mips64el' (e.g. mips64_linux)",
899    ),
900    (
901        "mips32",
902        "Architecture 'mips' is not supported\n  Hint: lamina implements the N64 ABI on 64-bit MIPS only, not O32. Write 'mips64' or 'mips64el' (e.g. mips64_linux)",
903    ),
904    (
905        "mips32el",
906        "Architecture 'mips' is not supported\n  Hint: lamina implements the N64 ABI on 64-bit MIPS only, not O32. Write 'mips64' or 'mips64el' (e.g. mips64_linux)",
907    ),
908    (
909        "s390",
910        "Architecture 's390' was not added\n  Hint: lamina supports 64-bit z/Architecture only. Write 's390x' (e.g. s390x_linux)",
911    ),
912];
913
914/// Shorthands for the fields of [`TargetArchitecture::spec`].
915///
916/// Each one reads the table; none of them is a second place a property is decided. A
917/// target that carries a variant should read [`crate::Target::spec`] instead, which
918/// applies the variant's refinements.
919impl TargetArchitecture {
920    pub const fn pointer_width(self) -> PointerWidth {
921        self.spec().pointer_width
922    }
923
924    pub const fn max_int_register_bits(self) -> u32 {
925        self.spec().max_int_register_bits
926    }
927
928    pub const fn has_64bit_int_registers(self) -> bool {
929        self.spec().has_64bit_int_registers()
930    }
931
932    pub const fn endianness(self) -> Endianness {
933        self.spec().endianness
934    }
935
936    pub const fn va_list(self) -> VaListAbi {
937        self.spec().va_list
938    }
939
940    pub const fn stack_alignment_at_call(self) -> usize {
941        self.spec().stack_alignment_at_call
942    }
943
944    pub const fn stack_page_size(self) -> usize {
945        self.spec().stack_page_size
946    }
947
948    pub const fn honors_argument_alignment(self) -> bool {
949        self.spec().honors_argument_alignment
950    }
951
952    pub const fn output_artifact(self) -> OutputArtifact {
953        self.spec().output_artifact
954    }
955
956    pub const fn data_layout(self) -> TargetDataLayout {
957        self.spec().data_layout()
958    }
959
960    pub const fn default_variant(self) -> ArchVariant {
961        self.spec().variants.default_variant()
962    }
963}