rustc_target/spec/
abi_map.rs

1use rustc_abi::{ArmCall, CanonAbi, ExternAbi, InterruptKind, X86Call};
2
3use crate::spec::Target;
4
5/// Mapping for ExternAbi to CanonAbi according to a Target
6///
7/// A maybe-transitional structure circa 2025 for hosting future experiments in
8/// encapsulating arch-specific ABI lowering details to make them more testable.
9#[derive(Clone, Debug)]
10pub struct AbiMap {
11    arch: Arch,
12    os: OsKind,
13}
14
15/// result from trying to map an ABI
16#[derive(Copy, Clone, Debug)]
17pub enum AbiMapping {
18    /// this ABI is exactly mapped for this platform
19    Direct(CanonAbi),
20    /// we don't yet warn on this, but we will
21    Deprecated(CanonAbi),
22    /// ABI we do not map for this platform: it must not reach codegen
23    Invalid,
24}
25
26impl AbiMapping {
27    /// optionally get a [CanonAbi], even if Deprecated
28    pub fn into_option(self) -> Option<CanonAbi> {
29        match self {
30            Self::Direct(abi) | Self::Deprecated(abi) => Some(abi),
31            Self::Invalid => None,
32        }
33    }
34
35    /// get a [CanonAbi] even if Deprecated, panicking if Invalid
36    #[track_caller]
37    pub fn unwrap(self) -> CanonAbi {
38        self.into_option().unwrap()
39    }
40
41    pub fn is_mapped(self) -> bool {
42        self.into_option().is_some()
43    }
44}
45
46impl AbiMap {
47    /// create an AbiMap according to arbitrary fields on the [Target]
48    pub fn from_target(target: &Target) -> Self {
49        // the purpose of this little exercise is to force listing what affects these mappings
50        let arch = match &*target.arch {
51            "aarch64" => Arch::Aarch64,
52            "amdgpu" => Arch::Amdgpu,
53            "arm" if target.llvm_target.starts_with("thumbv8m") => Arch::Arm(ArmVer::ThumbV8M),
54            "arm" => Arch::Arm(ArmVer::Other),
55            "avr" => Arch::Avr,
56            "msp430" => Arch::Msp430,
57            "nvptx64" => Arch::Nvptx,
58            "riscv32" | "riscv64" => Arch::Riscv,
59            "x86" => Arch::X86,
60            "x86_64" => Arch::X86_64,
61            _ => Arch::Other,
62        };
63
64        let os = if target.is_like_windows {
65            OsKind::Windows
66        } else if target.is_like_vexos {
67            OsKind::VEXos
68        } else {
69            OsKind::Other
70        };
71
72        AbiMap { arch, os }
73    }
74
75    /// lower an [ExternAbi] to a [CanonAbi] if this AbiMap allows
76    pub fn canonize_abi(&self, extern_abi: ExternAbi, has_c_varargs: bool) -> AbiMapping {
77        let AbiMap { os, arch } = *self;
78
79        let canon_abi = match (extern_abi, arch) {
80            // infallible lowerings
81            (ExternAbi::C { .. }, _) => CanonAbi::C,
82            (ExternAbi::Rust | ExternAbi::RustCall, _) => CanonAbi::Rust,
83            (ExternAbi::Unadjusted, _) => CanonAbi::C,
84
85            (ExternAbi::RustCold, _) if self.os == OsKind::Windows => CanonAbi::Rust,
86            (ExternAbi::RustCold, _) => CanonAbi::RustCold,
87
88            (ExternAbi::Custom, _) => CanonAbi::Custom,
89
90            (ExternAbi::System { .. }, Arch::X86) if os == OsKind::Windows && !has_c_varargs => {
91                CanonAbi::X86(X86Call::Stdcall)
92            }
93            (ExternAbi::System { .. }, Arch::Arm(..)) if self.os == OsKind::VEXos => {
94                // Calls to VEXos APIs do not use VFP registers.
95                CanonAbi::Arm(ArmCall::Aapcs)
96            }
97            (ExternAbi::System { .. }, _) => CanonAbi::C,
98
99            // fallible lowerings
100            /* multi-platform */
101            // always and forever
102            (ExternAbi::RustInvalid, _) => return AbiMapping::Invalid,
103
104            (ExternAbi::EfiApi, Arch::Arm(..)) => CanonAbi::Arm(ArmCall::Aapcs),
105            (ExternAbi::EfiApi, Arch::X86_64) => CanonAbi::X86(X86Call::Win64),
106            (ExternAbi::EfiApi, Arch::Aarch64 | Arch::Riscv | Arch::X86) => CanonAbi::C,
107            (ExternAbi::EfiApi, _) => return AbiMapping::Invalid,
108
109            /* arm */
110            (ExternAbi::Aapcs { .. }, Arch::Arm(..)) => CanonAbi::Arm(ArmCall::Aapcs),
111            (ExternAbi::Aapcs { .. }, _) => return AbiMapping::Invalid,
112
113            (ExternAbi::CmseNonSecureCall, Arch::Arm(ArmVer::ThumbV8M)) => {
114                CanonAbi::Arm(ArmCall::CCmseNonSecureCall)
115            }
116            (ExternAbi::CmseNonSecureEntry, Arch::Arm(ArmVer::ThumbV8M)) => {
117                CanonAbi::Arm(ArmCall::CCmseNonSecureEntry)
118            }
119            (ExternAbi::CmseNonSecureCall | ExternAbi::CmseNonSecureEntry, ..) => {
120                return AbiMapping::Invalid;
121            }
122
123            /* gpu */
124            (ExternAbi::PtxKernel, Arch::Nvptx) => CanonAbi::GpuKernel,
125            (ExternAbi::GpuKernel, Arch::Amdgpu | Arch::Nvptx) => CanonAbi::GpuKernel,
126            (ExternAbi::PtxKernel | ExternAbi::GpuKernel, _) => return AbiMapping::Invalid,
127
128            /* x86 */
129            (ExternAbi::Cdecl { .. }, Arch::X86) => CanonAbi::C,
130            (ExternAbi::Cdecl { .. }, _) => return AbiMapping::Deprecated(CanonAbi::C),
131
132            (ExternAbi::Fastcall { .. }, Arch::X86) => CanonAbi::X86(X86Call::Fastcall),
133            (ExternAbi::Fastcall { .. }, _) if os == OsKind::Windows => {
134                return AbiMapping::Deprecated(CanonAbi::C);
135            }
136            (ExternAbi::Fastcall { .. }, _) => return AbiMapping::Invalid,
137
138            (ExternAbi::Stdcall { .. }, Arch::X86) => CanonAbi::X86(X86Call::Stdcall),
139            (ExternAbi::Stdcall { .. }, _) if os == OsKind::Windows => {
140                return AbiMapping::Deprecated(CanonAbi::C);
141            }
142            (ExternAbi::Stdcall { .. }, _) => return AbiMapping::Invalid,
143
144            (ExternAbi::Thiscall { .. }, Arch::X86) => CanonAbi::X86(X86Call::Thiscall),
145            (ExternAbi::Thiscall { .. }, _) => return AbiMapping::Invalid,
146
147            (ExternAbi::Vectorcall { .. }, Arch::X86 | Arch::X86_64) => {
148                CanonAbi::X86(X86Call::Vectorcall)
149            }
150            (ExternAbi::Vectorcall { .. }, _) => return AbiMapping::Invalid,
151
152            (ExternAbi::SysV64 { .. }, Arch::X86_64) => CanonAbi::X86(X86Call::SysV64),
153            (ExternAbi::Win64 { .. }, Arch::X86_64) => CanonAbi::X86(X86Call::Win64),
154            (ExternAbi::SysV64 { .. } | ExternAbi::Win64 { .. }, _) => return AbiMapping::Invalid,
155
156            /* interrupts */
157            (ExternAbi::AvrInterrupt, Arch::Avr) => CanonAbi::Interrupt(InterruptKind::Avr),
158            (ExternAbi::AvrNonBlockingInterrupt, Arch::Avr) => {
159                CanonAbi::Interrupt(InterruptKind::AvrNonBlocking)
160            }
161            (ExternAbi::Msp430Interrupt, Arch::Msp430) => {
162                CanonAbi::Interrupt(InterruptKind::Msp430)
163            }
164            (ExternAbi::RiscvInterruptM, Arch::Riscv) => {
165                CanonAbi::Interrupt(InterruptKind::RiscvMachine)
166            }
167            (ExternAbi::RiscvInterruptS, Arch::Riscv) => {
168                CanonAbi::Interrupt(InterruptKind::RiscvSupervisor)
169            }
170            (ExternAbi::X86Interrupt, Arch::X86 | Arch::X86_64) => {
171                CanonAbi::Interrupt(InterruptKind::X86)
172            }
173            (
174                ExternAbi::AvrInterrupt
175                | ExternAbi::AvrNonBlockingInterrupt
176                | ExternAbi::Msp430Interrupt
177                | ExternAbi::RiscvInterruptM
178                | ExternAbi::RiscvInterruptS
179                | ExternAbi::X86Interrupt,
180                _,
181            ) => return AbiMapping::Invalid,
182        };
183
184        AbiMapping::Direct(canon_abi)
185    }
186}
187
188#[derive(Debug, PartialEq, Copy, Clone)]
189enum Arch {
190    Aarch64,
191    Amdgpu,
192    Arm(ArmVer),
193    Avr,
194    Msp430,
195    Nvptx,
196    Riscv,
197    X86,
198    X86_64,
199    /// Architectures which don't need other considerations for ABI lowering
200    Other,
201}
202
203#[derive(Debug, PartialEq, Copy, Clone)]
204enum OsKind {
205    Windows,
206    VEXos,
207    Other,
208}
209
210#[derive(Debug, PartialEq, Copy, Clone)]
211enum ArmVer {
212    ThumbV8M,
213    Other,
214}