rucc_tuple/abi.rs
1//! The calling convention variant and the object format.
2
3use core::fmt;
4
5use crate::{Arch, DataModel, Env, Os, SubArch};
6
7/// The variant of the platform ABI that decides where floating point arguments go.
8///
9/// This is a separate field from the environment because on ARM it is genuinely orthogonal to
10/// the libc, and because it is the clearest case of `spec/cross-compile/03-target-model.md`'s rule: it changes
11/// how a function is called, so it is in the tuple. GCC fuses it into the environment component
12/// as `gnueabihf`, and [`crate::TargetTuple`] reproduces that spelling on output rather than
13/// storing the fused form.
14///
15/// The names are the ones the RISC-V and LoongArch `-mabi=` flags use, minus the data model
16/// prefix that those flags carry redundantly. `-mabi=lp64d` is
17/// [`DataModel::Lp64`](crate::DataModel::Lp64) plus [`Abi::DoubleFloat`], and splitting it means
18/// the data model is written down once.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
20pub enum Abi {
21 /// The psABI default for this target. Resolved by [`Abi::resolve`], which is what code
22 /// generation should call rather than matching on this variant.
23 #[default]
24 Default,
25 /// Floating point arguments in integer registers, and no FPU assumed. `-mfloat-abi=soft`,
26 /// `-mabi=lp64`, `-mabi=ilp32`.
27 SoftFloat,
28 /// Floating point arguments in integer registers, but FPU instructions are emitted for
29 /// arithmetic. ARM's `softfp`, which is ABI compatible with soft float and faster.
30 SoftFp,
31 /// Single precision floating point arguments in float registers. `-mabi=lp64f`. RISC-V
32 /// only; no target in the matrix uses it, and it exists because leaving it out would make
33 /// the enumeration a lie about the ABI space.
34 SingleFloat,
35 /// Double precision floating point arguments in float registers. `-mfloat-abi=hard`,
36 /// `-mabi=lp64d`, `-mabi=ilp32d`.
37 DoubleFloat,
38}
39
40impl Abi {
41 /// The name used in diagnostics, in `--print-config` and in `-mabi=` reconstruction.
42 pub const fn as_str(self) -> &'static str {
43 match self {
44 Abi::Default => "default",
45 Abi::SoftFloat => "soft",
46 Abi::SoftFp => "softfp",
47 Abi::SingleFloat => "single",
48 Abi::DoubleFloat => "hard",
49 }
50 }
51
52 /// The concrete ABI for a target that did not name one.
53 ///
54 /// Every case here is a psABI reading rather than a preference. AArch64, x86-64 and Darwin
55 /// have one float ABI so there is nothing to choose. RISC-V and LoongArch Linux are LP64D by
56 /// convention and by every distribution's build. Bare metal ARM is soft float because the
57 /// core may have no FPU, and ARM Linux is hard float because every ARM distribution shipping
58 /// today is, which is why the matrix rows are all `eabihf`.
59 pub const fn resolve(self, arch: Arch, sub_arch: SubArch, os: Os, env: Env) -> Abi {
60 if !matches!(self, Abi::Default) {
61 return self;
62 }
63 match arch {
64 Arch::Arm => {
65 if sub_arch.is_thumb_only() || matches!(os, Os::None) {
66 Abi::SoftFloat
67 } else if matches!(env, Env::Gnu | Env::Musl | Env::Android) {
68 Abi::DoubleFloat
69 } else {
70 Abi::SoftFloat
71 }
72 }
73 Arch::Riscv64 | Arch::Riscv32 | Arch::LoongArch64 => match os {
74 Os::None => Abi::SoftFloat,
75 _ => Abi::DoubleFloat,
76 },
77 _ => Abi::DoubleFloat,
78 }
79 }
80
81 /// Whether this ABI may be named for this architecture.
82 ///
83 /// Naming a float ABI on x86-64 is a spelling error rather than a configuration, because
84 /// SysV AMD64 has one convention and there is nothing to select. Saying so is the whole
85 /// point of validating the tuple: the alternative is accepting the flag and ignoring it.
86 pub const fn is_valid_for(self, arch: Arch) -> bool {
87 match self {
88 Abi::Default => true,
89 Abi::SoftFp => matches!(arch, Arch::Arm),
90 Abi::SingleFloat => matches!(arch, Arch::Riscv64 | Arch::Riscv32),
91 Abi::SoftFloat | Abi::DoubleFloat => arch.selects_float_abi(),
92 }
93 }
94
95 /// The `-mabi=` value GCC would take for this ABI on this architecture, which is what the
96 /// driver has to reproduce when it hands work to an external assembler or linker.
97 pub fn to_mabi(self, arch: Arch, model: DataModel) -> Option<String> {
98 let resolved = self;
99 match arch {
100 Arch::Riscv64 | Arch::Riscv32 | Arch::LoongArch64 => {
101 let base = match model {
102 DataModel::Lp64 => "lp64",
103 DataModel::Ilp32 | DataModel::Ilp32On64 => "ilp32",
104 DataModel::Llp64 => return None,
105 };
106 let suffix = match resolved {
107 Abi::SoftFloat => "",
108 Abi::SingleFloat => "f",
109 Abi::DoubleFloat => "d",
110 Abi::Default | Abi::SoftFp => return None,
111 };
112 Some(format!("{base}{suffix}"))
113 }
114 _ => None,
115 }
116 }
117}
118
119impl fmt::Display for Abi {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 f.write_str(self.as_str())
122 }
123}
124
125/// The container the compiler writes.
126///
127/// One per OS, with freestanding taking its format from the architecture. rucc writes all four
128/// itself rather than handing text to an assembler, which is why this is a first class field:
129/// every one of them is a different section model, a different relocation table and a different
130/// symbol table.
131#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
132pub enum ObjectFormat {
133 /// ELF. Linux, the BSDs, illumos, and freestanding on every architecture but wasm.
134 Elf,
135 /// Mach-O. Darwin, and the format with a version treadmill: chained fixups and
136 /// `__init_offsets` are recent and are not optional on current systems.
137 MachO,
138 /// COFF, in its PE flavour. Windows, both environments, with unwind information in
139 /// `.pdata` and `.xdata` rather than in a DWARF section.
140 Coff,
141 /// The WebAssembly object format. A different shape from the other three, and
142 /// `spec/cross-compile/05-architectures.md` section 5.9 treats reaching it as a second back end.
143 Wasm,
144}
145
146impl ObjectFormat {
147 /// The name used in diagnostics and in `--print-config`.
148 pub const fn as_str(self) -> &'static str {
149 match self {
150 ObjectFormat::Elf => "elf",
151 ObjectFormat::MachO => "macho",
152 ObjectFormat::Coff => "coff",
153 ObjectFormat::Wasm => "wasm",
154 }
155 }
156
157 /// The suffix an object file gets on this format.
158 pub const fn object_extension(self) -> &'static str {
159 match self {
160 ObjectFormat::Coff => "obj",
161 _ => "o",
162 }
163 }
164
165 /// The suffix a static archive gets.
166 pub const fn archive_extension(self) -> &'static str {
167 match self {
168 ObjectFormat::Coff => "lib",
169 _ => "a",
170 }
171 }
172
173 /// Whether symbols are prefixed with an underscore.
174 ///
175 /// True on Mach-O and on 32-bit COFF, false on ELF and 64-bit COFF. Getting this wrong
176 /// produces a link error naming a symbol that is visibly present in the object, which is one
177 /// of the more confusing ways to spend an afternoon.
178 pub const fn leading_underscore(self, data_model: DataModel) -> bool {
179 match self {
180 ObjectFormat::MachO => true,
181 ObjectFormat::Coff => matches!(data_model, DataModel::Ilp32),
182 ObjectFormat::Elf | ObjectFormat::Wasm => false,
183 }
184 }
185}
186
187impl fmt::Display for ObjectFormat {
188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189 f.write_str(self.as_str())
190 }
191}