rustc_abi/
extern_abi.rs

1use std::cmp::Ordering;
2use std::fmt;
3use std::hash::{Hash, Hasher};
4
5#[cfg(feature = "nightly")]
6use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd};
7#[cfg(feature = "nightly")]
8use rustc_macros::{Decodable, Encodable};
9#[cfg(feature = "nightly")]
10use rustc_span::Symbol;
11
12use crate::AbiFromStrErr;
13
14#[cfg(test)]
15mod tests;
16
17/// ABI we expect to see within `extern "{abi}"`
18#[derive(Clone, Copy, Debug)]
19#[cfg_attr(feature = "nightly", derive(Encodable, Decodable))]
20pub enum ExternAbi {
21    /* universal */
22    /// presumed C ABI for the platform
23    C {
24        unwind: bool,
25    },
26    /// ABI of the "system" interface, e.g. the Win32 API, always "aliasing"
27    System {
28        unwind: bool,
29    },
30
31    /// that's us!
32    Rust,
33    /// the mostly-unused `unboxed_closures` ABI, effectively now an impl detail unless someone
34    /// puts in the work to make it viable again... but would we need a special ABI?
35    RustCall,
36    /// For things unlikely to be called, where reducing register pressure in
37    /// `extern "Rust"` callers is worth paying extra cost in the callee.
38    /// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
39    RustCold,
40
41    /// An always-invalid ABI that's used to test "this ABI is not supported by this platform"
42    /// in a platform-agnostic way.
43    RustInvalid,
44
45    /// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM.
46    /// Even normally-compatible Rust types can become ABI-incompatible with this ABI!
47    Unadjusted,
48
49    /// An ABI that rustc does not know how to call or define. Functions with this ABI can
50    /// only be created using `#[naked]` functions or `extern "custom"` blocks, and can only
51    /// be called from inline assembly.
52    Custom,
53
54    /// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias
55    /// and only valid on platforms that have a UEFI standard
56    EfiApi,
57
58    /* arm */
59    /// Arm Architecture Procedure Call Standard, sometimes `ExternAbi::C` is an alias for this
60    Aapcs {
61        unwind: bool,
62    },
63    /// extremely constrained barely-C ABI for TrustZone
64    CmseNonSecureCall,
65    /// extremely constrained barely-C ABI for TrustZone
66    CmseNonSecureEntry,
67
68    /* gpu */
69    /// An entry-point function called by the GPU's host
70    GpuKernel,
71    /// An entry-point function called by the GPU's host
72    // FIXME: why do we have two of these?
73    PtxKernel,
74
75    /* interrupt */
76    AvrInterrupt,
77    AvrNonBlockingInterrupt,
78    Msp430Interrupt,
79    RiscvInterruptM,
80    RiscvInterruptS,
81    X86Interrupt,
82
83    /* x86 */
84    /// `ExternAbi::C` but spelled funny because x86
85    Cdecl {
86        unwind: bool,
87    },
88    /// gnu-stdcall on "unix" and win-stdcall on "windows"
89    Stdcall {
90        unwind: bool,
91    },
92    /// gnu-fastcall on "unix" and win-fastcall on "windows"
93    Fastcall {
94        unwind: bool,
95    },
96    /// windows C++ ABI
97    Thiscall {
98        unwind: bool,
99    },
100    /// uses AVX and stuff
101    Vectorcall {
102        unwind: bool,
103    },
104
105    /* x86_64 */
106    SysV64 {
107        unwind: bool,
108    },
109    Win64 {
110        unwind: bool,
111    },
112}
113
114macro_rules! abi_impls {
115    ($e_name:ident = {
116        $($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)*
117    }) => {
118        impl $e_name {
119            pub const ALL_VARIANTS: &[Self] = &[
120                $($e_name::$variant $({ unwind: $uw })*,)*
121            ];
122            pub const fn as_str(&self) -> &'static str {
123                match self {
124                    $($e_name::$variant $( { unwind: $uw } )* => $tok,)*
125                }
126            }
127        }
128
129        impl ::core::str::FromStr for $e_name {
130            type Err = AbiFromStrErr;
131            fn from_str(s: &str) -> Result<$e_name, Self::Err> {
132                match s {
133                    $($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*
134                    _ => Err(AbiFromStrErr::Unknown),
135                }
136            }
137        }
138    }
139}
140
141abi_impls! {
142    ExternAbi = {
143            C { unwind: false } =><= "C",
144            C { unwind: true } =><= "C-unwind",
145            Rust =><= "Rust",
146            Aapcs { unwind: false } =><= "aapcs",
147            Aapcs { unwind: true } =><= "aapcs-unwind",
148            AvrInterrupt =><= "avr-interrupt",
149            AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",
150            Cdecl { unwind: false } =><= "cdecl",
151            Cdecl { unwind: true } =><= "cdecl-unwind",
152            CmseNonSecureCall =><= "cmse-nonsecure-call",
153            CmseNonSecureEntry =><= "cmse-nonsecure-entry",
154            Custom =><= "custom",
155            EfiApi =><= "efiapi",
156            Fastcall { unwind: false } =><= "fastcall",
157            Fastcall { unwind: true } =><= "fastcall-unwind",
158            GpuKernel =><= "gpu-kernel",
159            Msp430Interrupt =><= "msp430-interrupt",
160            PtxKernel =><= "ptx-kernel",
161            RiscvInterruptM =><= "riscv-interrupt-m",
162            RiscvInterruptS =><= "riscv-interrupt-s",
163            RustCall =><= "rust-call",
164            RustCold =><= "rust-cold",
165            RustInvalid =><= "rust-invalid",
166            Stdcall { unwind: false } =><= "stdcall",
167            Stdcall { unwind: true } =><= "stdcall-unwind",
168            System { unwind: false } =><= "system",
169            System { unwind: true } =><= "system-unwind",
170            SysV64 { unwind: false } =><= "sysv64",
171            SysV64 { unwind: true } =><= "sysv64-unwind",
172            Thiscall { unwind: false } =><= "thiscall",
173            Thiscall { unwind: true } =><= "thiscall-unwind",
174            Unadjusted =><= "unadjusted",
175            Vectorcall { unwind: false } =><= "vectorcall",
176            Vectorcall { unwind: true } =><= "vectorcall-unwind",
177            Win64 { unwind: false } =><= "win64",
178            Win64 { unwind: true } =><= "win64-unwind",
179            X86Interrupt =><= "x86-interrupt",
180    }
181}
182
183impl Ord for ExternAbi {
184    fn cmp(&self, rhs: &Self) -> Ordering {
185        self.as_str().cmp(rhs.as_str())
186    }
187}
188
189impl PartialOrd for ExternAbi {
190    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
191        Some(self.cmp(rhs))
192    }
193}
194
195impl PartialEq for ExternAbi {
196    fn eq(&self, rhs: &Self) -> bool {
197        self.cmp(rhs) == Ordering::Equal
198    }
199}
200
201impl Eq for ExternAbi {}
202
203impl Hash for ExternAbi {
204    fn hash<H: Hasher>(&self, state: &mut H) {
205        self.as_str().hash(state);
206        // double-assurance of a prefix breaker
207        u32::from_be_bytes(*b"ABI\0").hash(state);
208    }
209}
210
211#[cfg(feature = "nightly")]
212impl<C> HashStable<C> for ExternAbi {
213    #[inline]
214    fn hash_stable(&self, _: &mut C, hasher: &mut StableHasher) {
215        Hash::hash(self, hasher);
216    }
217}
218
219#[cfg(feature = "nightly")]
220impl StableOrd for ExternAbi {
221    const CAN_USE_UNSTABLE_SORT: bool = true;
222
223    // because each ABI is hashed like a string, there is no possible instability
224    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
225}
226
227#[cfg(feature = "nightly")]
228rustc_error_messages::into_diag_arg_using_display!(ExternAbi);
229
230#[cfg(feature = "nightly")]
231pub enum CVariadicStatus {
232    NotSupported,
233    Stable,
234    Unstable { feature: Symbol },
235}
236
237impl ExternAbi {
238    /// An ABI "like Rust"
239    ///
240    /// These ABIs are fully controlled by the Rust compiler, which means they
241    /// - support unwinding with `-Cpanic=unwind`, unlike `extern "C"`
242    /// - often diverge from the C ABI
243    /// - are subject to change between compiler versions
244    pub fn is_rustic_abi(self) -> bool {
245        use ExternAbi::*;
246        matches!(self, Rust | RustCall | RustCold)
247    }
248
249    /// Returns whether the ABI supports C variadics. This only controls whether we allow *imports*
250    /// of such functions via `extern` blocks; there's a separate check during AST construction
251    /// guarding *definitions* of variadic functions.
252    #[cfg(feature = "nightly")]
253    pub fn supports_c_variadic(self) -> CVariadicStatus {
254        // * C and Cdecl obviously support varargs.
255        // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.
256        // * EfiApi is based on Win64 or C, so it also supports it.
257        // * System automatically falls back to C when used with variadics, therefore supports it.
258        //
259        // * Stdcall does not, because it would be impossible for the callee to clean
260        //   up the arguments. (callee doesn't know how many arguments are there)
261        // * Same for Fastcall, Vectorcall and Thiscall.
262        // * Other calling conventions are related to hardware or the compiler itself.
263        //
264        // All of the supported ones must have a test in `tests/codegen/cffi/c-variadic-ffi.rs`.
265        match self {
266            Self::C { .. }
267            | Self::Cdecl { .. }
268            | Self::Aapcs { .. }
269            | Self::Win64 { .. }
270            | Self::SysV64 { .. }
271            | Self::EfiApi
272            | Self::System { .. } => CVariadicStatus::Stable,
273            _ => CVariadicStatus::NotSupported,
274        }
275    }
276
277    /// Returns whether the ABI supports guaranteed tail calls.
278    #[cfg(feature = "nightly")]
279    pub fn supports_guaranteed_tail_call(self) -> bool {
280        match self {
281            Self::CmseNonSecureCall | Self::CmseNonSecureEntry => {
282                // See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers
283                // before returning, and hence cannot guarantee a tail call.
284                false
285            }
286            Self::AvrInterrupt
287            | Self::AvrNonBlockingInterrupt
288            | Self::Msp430Interrupt
289            | Self::RiscvInterruptM
290            | Self::RiscvInterruptS
291            | Self::X86Interrupt => {
292                // See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly.
293                false
294            }
295            Self::GpuKernel | Self::PtxKernel => {
296                // See https://godbolt.org/z/jq5TE5jK1.
297                false
298            }
299            Self::Custom => {
300                // This ABI does not support calls at all (except via assembly).
301                false
302            }
303            Self::C { .. }
304            | Self::System { .. }
305            | Self::Rust
306            | Self::RustCall
307            | Self::RustCold
308            | Self::RustInvalid
309            | Self::Unadjusted
310            | Self::EfiApi
311            | Self::Aapcs { .. }
312            | Self::Cdecl { .. }
313            | Self::Stdcall { .. }
314            | Self::Fastcall { .. }
315            | Self::Thiscall { .. }
316            | Self::Vectorcall { .. }
317            | Self::SysV64 { .. }
318            | Self::Win64 { .. } => true,
319        }
320    }
321}
322
323pub fn all_names() -> Vec<&'static str> {
324    ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect()
325}
326
327impl ExternAbi {
328    /// Default ABI chosen for `extern fn` declarations without an explicit ABI.
329    pub const FALLBACK: ExternAbi = ExternAbi::C { unwind: false };
330
331    pub fn name(self) -> &'static str {
332        self.as_str()
333    }
334}
335
336impl fmt::Display for ExternAbi {
337    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338        write!(f, "\"{}\"", self.as_str())
339    }
340}