Skip to main content

lamina_codegen/
target_support.rs

1//! Cross-platform target support for code generation.
2//!
3//! This module defines which (architecture, OS) combinations are supported
4//! for assembly and binary emission, so callers can cross-compile to a
5//! different target than the host.
6
7use lamina_platform::{TargetArchitecture, TargetOperatingSystem};
8
9/// Whether a backend can *write* assembly text for this (arch, os).
10///
11/// Distinct from `ras::is_assembly_target_supported`, which answers whether ras can
12/// *read* assembly for a target and turn it into an object file. A target can be one
13/// and not the other.
14///
15/// Cross-compilation: pass the desired target, not the host.
16pub fn is_assembly_emission_supported(arch: TargetArchitecture, os: TargetOperatingSystem) -> bool {
17    match arch {
18        TargetArchitecture::Nvptx64 => os == TargetOperatingSystem::Cuda,
19        TargetArchitecture::Amdgcn => os == TargetOperatingSystem::AmdHsa,
20        TargetArchitecture::X86_64 => os_supported_for_x86_64(os),
21        TargetArchitecture::Aarch64 => os_supported_for_aarch64(os),
22        #[cfg(feature = "nightly")]
23        TargetArchitecture::Arx64 => os_supported_for_arx64(os),
24        TargetArchitecture::Riscv32 | TargetArchitecture::Riscv64 => os_supported_for_riscv(os),
25        TargetArchitecture::LoongArch64 => os == TargetOperatingSystem::Linux,
26        TargetArchitecture::S390x => os == TargetOperatingSystem::Linux,
27        TargetArchitecture::Mips64 | TargetArchitecture::Mips64El => {
28            os == TargetOperatingSystem::Linux
29        }
30        TargetArchitecture::I686 => os == TargetOperatingSystem::Linux,
31        // wasi_snapshot_preview1 passes pointers as i32, so it has no wasm64 form.
32        TargetArchitecture::Wasm32 => {
33            matches!(
34                os,
35                TargetOperatingSystem::Unknown
36                    | TargetOperatingSystem::Linux
37                    | TargetOperatingSystem::Wasi
38            )
39        }
40        TargetArchitecture::Wasm64 => {
41            matches!(
42                os,
43                TargetOperatingSystem::Unknown | TargetOperatingSystem::Linux
44            )
45        }
46        TargetArchitecture::Armv7 => os == TargetOperatingSystem::Linux,
47        TargetArchitecture::Thumbv6m | TargetArchitecture::Thumbv7m => {
48            matches!(
49                os,
50                TargetOperatingSystem::Unknown | TargetOperatingSystem::Linux
51            )
52        }
53        TargetArchitecture::PowerPC64 => {
54            matches!(
55                os,
56                TargetOperatingSystem::Linux | TargetOperatingSystem::Unknown
57            )
58        }
59        TargetArchitecture::PowerPC64Be => os == TargetOperatingSystem::Linux,
60        TargetArchitecture::Arm32 => false,
61        #[cfg(feature = "nightly")]
62        TargetArchitecture::Riscv128 | TargetArchitecture::Lisa => false,
63    }
64}
65
66/// Returns true if the OS is supported for x86_64 code generation.
67fn os_supported_for_x86_64(os: TargetOperatingSystem) -> bool {
68    matches!(
69        os,
70        TargetOperatingSystem::Linux
71            | TargetOperatingSystem::Android
72            | TargetOperatingSystem::MacOS
73            | TargetOperatingSystem::IOS
74            | TargetOperatingSystem::TvOS
75            | TargetOperatingSystem::WatchOS
76            | TargetOperatingSystem::VisionOS
77            | TargetOperatingSystem::Windows
78            | TargetOperatingSystem::FreeBSD
79            | TargetOperatingSystem::OpenBSD
80            | TargetOperatingSystem::NetBSD
81            | TargetOperatingSystem::DragonFly
82            | TargetOperatingSystem::Redox
83            | TargetOperatingSystem::Unknown
84    )
85}
86
87/// Returns true if the OS is supported for AArch64 code generation.
88fn os_supported_for_aarch64(os: TargetOperatingSystem) -> bool {
89    matches!(
90        os,
91        TargetOperatingSystem::Linux
92            | TargetOperatingSystem::Android
93            | TargetOperatingSystem::MacOS
94            | TargetOperatingSystem::IOS
95            | TargetOperatingSystem::TvOS
96            | TargetOperatingSystem::WatchOS
97            | TargetOperatingSystem::VisionOS
98            | TargetOperatingSystem::Windows
99            | TargetOperatingSystem::FreeBSD
100            | TargetOperatingSystem::OpenBSD
101            | TargetOperatingSystem::NetBSD
102            | TargetOperatingSystem::DragonFly
103            | TargetOperatingSystem::Redox
104            | TargetOperatingSystem::Unknown
105    )
106}
107
108/// Returns true if the OS is supported for ARX64 code generation.
109#[cfg(feature = "nightly")]
110fn os_supported_for_arx64(os: TargetOperatingSystem) -> bool {
111    matches!(os, TargetOperatingSystem::Unknown)
112}
113
114/// Returns true if the OS is supported for RISC-V code generation.
115fn os_supported_for_riscv(os: TargetOperatingSystem) -> bool {
116    matches!(
117        os,
118        TargetOperatingSystem::Linux
119            | TargetOperatingSystem::FreeBSD
120            | TargetOperatingSystem::OpenBSD
121            | TargetOperatingSystem::NetBSD
122            | TargetOperatingSystem::DragonFly
123            | TargetOperatingSystem::Unknown
124    )
125}
126
127/// Returns a short description of supported targets for error messages.
128pub fn supported_assembly_targets_hint() -> &'static str {
129    "Supported: x86_64 and aarch64 on Linux, Android, macOS, iOS, Windows, BSD, Redox and bare metal; \n riscv32/riscv64 on Linux, BSD and bare metal; i686, armv7, ppc64le, ppc64, s390x, mips64, mips64el \n and loongarch64 on Linux; thumbv6m/thumbv7m on Linux and bare metal; wasm32 on Linux, WASI and \n bare metal, wasm64 on Linux and bare metal; nvptx64 on CUDA and amdgcn on AMDHSA. \n Use lamina_platform::Target for cross-compilation."
130}
131
132/// Returns true if the given OS uses ELF object format (Linux, BSD, Redox, Horizon, Orbis, Prospero).
133pub fn os_uses_elf(os: TargetOperatingSystem) -> bool {
134    matches!(
135        os,
136        TargetOperatingSystem::Linux
137            | TargetOperatingSystem::Android
138            | TargetOperatingSystem::FreeBSD
139            | TargetOperatingSystem::OpenBSD
140            | TargetOperatingSystem::NetBSD
141            | TargetOperatingSystem::DragonFly
142            | TargetOperatingSystem::Redox
143            | TargetOperatingSystem::Horizon
144            | TargetOperatingSystem::Orbis
145            | TargetOperatingSystem::Prospero
146    )
147}
148
149/// Returns true if the given OS uses Mach-O (macOS).
150pub fn os_uses_macho(os: TargetOperatingSystem) -> bool {
151    matches!(
152        os,
153        TargetOperatingSystem::MacOS
154            | TargetOperatingSystem::IOS
155            | TargetOperatingSystem::TvOS
156            | TargetOperatingSystem::WatchOS
157            | TargetOperatingSystem::VisionOS
158    )
159}
160
161/// Returns true if the given OS uses COFF/PE (Windows).
162pub fn os_uses_coff(os: TargetOperatingSystem) -> bool {
163    os == TargetOperatingSystem::Windows
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    #[test]
171    fn test_x86_64_cross_platform() {
172        assert!(is_assembly_emission_supported(
173            TargetArchitecture::X86_64,
174            TargetOperatingSystem::Linux
175        ));
176        assert!(is_assembly_emission_supported(
177            TargetArchitecture::X86_64,
178            TargetOperatingSystem::Windows
179        ));
180        assert!(is_assembly_emission_supported(
181            TargetArchitecture::X86_64,
182            TargetOperatingSystem::MacOS
183        ));
184        assert!(is_assembly_emission_supported(
185            TargetArchitecture::X86_64,
186            TargetOperatingSystem::FreeBSD
187        ));
188    }
189
190    #[test]
191    fn test_aarch64_cross_platform() {
192        assert!(is_assembly_emission_supported(
193            TargetArchitecture::Aarch64,
194            TargetOperatingSystem::Linux
195        ));
196        assert!(is_assembly_emission_supported(
197            TargetArchitecture::Aarch64,
198            TargetOperatingSystem::MacOS
199        ));
200    }
201
202    #[test]
203    fn test_riscv_elf_like_only() {
204        assert!(is_assembly_emission_supported(
205            TargetArchitecture::Riscv64,
206            TargetOperatingSystem::Linux
207        ));
208        assert!(!is_assembly_emission_supported(
209            TargetArchitecture::Riscv64,
210            TargetOperatingSystem::Windows
211        ));
212    }
213
214    #[test]
215    fn test_wasm_limited_os() {
216        assert!(is_assembly_emission_supported(
217            TargetArchitecture::Wasm32,
218            TargetOperatingSystem::Unknown
219        ));
220        assert!(is_assembly_emission_supported(
221            TargetArchitecture::Wasm32,
222            TargetOperatingSystem::Wasi
223        ));
224        assert!(!is_assembly_emission_supported(
225            TargetArchitecture::Wasm64,
226            TargetOperatingSystem::Wasi
227        ));
228        assert!(!is_assembly_emission_supported(
229            TargetArchitecture::X86_64,
230            TargetOperatingSystem::Wasi
231        ));
232    }
233
234    #[test]
235    fn test_unknown_os_fallback() {
236        assert!(is_assembly_emission_supported(
237            TargetArchitecture::X86_64,
238            TargetOperatingSystem::Unknown
239        ));
240        assert!(is_assembly_emission_supported(
241            TargetArchitecture::Aarch64,
242            TargetOperatingSystem::Unknown
243        ));
244    }
245
246    #[test]
247    fn test_apple_mobile_assembly_supported() {
248        for os in [
249            TargetOperatingSystem::IOS,
250            TargetOperatingSystem::TvOS,
251            TargetOperatingSystem::WatchOS,
252            TargetOperatingSystem::VisionOS,
253        ] {
254            assert!(
255                is_assembly_emission_supported(TargetArchitecture::X86_64, os),
256                "{os}"
257            );
258            assert!(
259                is_assembly_emission_supported(TargetArchitecture::Aarch64, os),
260                "{os}"
261            );
262        }
263    }
264
265    #[test]
266    fn test_android_is_elf_and_assembly_supported() {
267        assert!(is_assembly_emission_supported(
268            TargetArchitecture::X86_64,
269            TargetOperatingSystem::Android
270        ));
271        assert!(is_assembly_emission_supported(
272            TargetArchitecture::Aarch64,
273            TargetOperatingSystem::Android
274        ));
275        assert!(os_uses_elf(TargetOperatingSystem::Android));
276    }
277
278    #[test]
279    fn test_apple_mobile_oses_use_macho() {
280        for os in [
281            TargetOperatingSystem::IOS,
282            TargetOperatingSystem::TvOS,
283            TargetOperatingSystem::WatchOS,
284            TargetOperatingSystem::VisionOS,
285        ] {
286            assert!(os_uses_macho(os), "{os} should use Mach-O");
287            assert!(!os_uses_elf(os), "{os} should not use ELF");
288        }
289    }
290
291    #[test]
292    fn test_unsupported_arch() {
293        assert!(!is_assembly_emission_supported(
294            TargetArchitecture::Arm32,
295            TargetOperatingSystem::Linux
296        ));
297    }
298
299    #[test]
300    fn test_powerpc64_assembly_supported() {
301        assert!(is_assembly_emission_supported(
302            TargetArchitecture::PowerPC64,
303            TargetOperatingSystem::Linux
304        ));
305        assert!(is_assembly_emission_supported(
306            TargetArchitecture::PowerPC64,
307            TargetOperatingSystem::Unknown
308        ));
309        assert!(is_assembly_emission_supported(
310            TargetArchitecture::PowerPC64Be,
311            TargetOperatingSystem::Linux
312        ));
313        assert!(!is_assembly_emission_supported(
314            TargetArchitecture::PowerPC64Be,
315            TargetOperatingSystem::Windows
316        ));
317    }
318
319    #[test]
320    fn test_os_uses_elf() {
321        assert!(os_uses_elf(TargetOperatingSystem::Linux));
322        assert!(os_uses_elf(TargetOperatingSystem::FreeBSD));
323        assert!(os_uses_elf(TargetOperatingSystem::Redox));
324        assert!(os_uses_elf(TargetOperatingSystem::Horizon));
325        assert!(os_uses_elf(TargetOperatingSystem::Orbis));
326        assert!(os_uses_elf(TargetOperatingSystem::Prospero));
327        assert!(!os_uses_elf(TargetOperatingSystem::MacOS));
328        assert!(!os_uses_elf(TargetOperatingSystem::Windows));
329    }
330}