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    // FIXME: should not be callable from Rust on GPU targets, is for host's use only
71    GpuKernel,
72    /// An entry-point function called by the GPU's host
73    // FIXME: why do we have two of these?
74    PtxKernel,
75
76    /* interrupt */
77    AvrInterrupt,
78    AvrNonBlockingInterrupt,
79    Msp430Interrupt,
80    RiscvInterruptM,
81    RiscvInterruptS,
82    X86Interrupt,
83
84    /* x86 */
85    /// `ExternAbi::C` but spelled funny because x86
86    Cdecl {
87        unwind: bool,
88    },
89    /// gnu-stdcall on "unix" and win-stdcall on "windows"
90    Stdcall {
91        unwind: bool,
92    },
93    /// gnu-fastcall on "unix" and win-fastcall on "windows"
94    Fastcall {
95        unwind: bool,
96    },
97    /// windows C++ ABI
98    Thiscall {
99        unwind: bool,
100    },
101    /// uses AVX and stuff
102    Vectorcall {
103        unwind: bool,
104    },
105
106    /* x86_64 */
107    SysV64 {
108        unwind: bool,
109    },
110    Win64 {
111        unwind: bool,
112    },
113}
114
115macro_rules! abi_impls {
116    ($e_name:ident = {
117        $($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)*
118    }) => {
119        impl $e_name {
120            pub const ALL_VARIANTS: &[Self] = &[
121                $($e_name::$variant $({ unwind: $uw })*,)*
122            ];
123            pub const fn as_str(&self) -> &'static str {
124                match self {
125                    $($e_name::$variant $( { unwind: $uw } )* => $tok,)*
126                }
127            }
128        }
129
130        impl ::core::str::FromStr for $e_name {
131            type Err = AbiFromStrErr;
132            fn from_str(s: &str) -> Result<$e_name, Self::Err> {
133                match s {
134                    $($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*
135                    _ => Err(AbiFromStrErr::Unknown),
136                }
137            }
138        }
139    }
140}
141
142abi_impls! {
143    ExternAbi = {
144            C { unwind: false } =><= "C",
145            C { unwind: true } =><= "C-unwind",
146            Rust =><= "Rust",
147            Aapcs { unwind: false } =><= "aapcs",
148            Aapcs { unwind: true } =><= "aapcs-unwind",
149            AvrInterrupt =><= "avr-interrupt",
150            AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",
151            Cdecl { unwind: false } =><= "cdecl",
152            Cdecl { unwind: true } =><= "cdecl-unwind",
153            CmseNonSecureCall =><= "cmse-nonsecure-call",
154            CmseNonSecureEntry =><= "cmse-nonsecure-entry",
155            Custom =><= "custom",
156            EfiApi =><= "efiapi",
157            Fastcall { unwind: false } =><= "fastcall",
158            Fastcall { unwind: true } =><= "fastcall-unwind",
159            GpuKernel =><= "gpu-kernel",
160            Msp430Interrupt =><= "msp430-interrupt",
161            PtxKernel =><= "ptx-kernel",
162            RiscvInterruptM =><= "riscv-interrupt-m",
163            RiscvInterruptS =><= "riscv-interrupt-s",
164            RustCall =><= "rust-call",
165            RustCold =><= "rust-cold",
166            RustInvalid =><= "rust-invalid",
167            Stdcall { unwind: false } =><= "stdcall",
168            Stdcall { unwind: true } =><= "stdcall-unwind",
169            System { unwind: false } =><= "system",
170            System { unwind: true } =><= "system-unwind",
171            SysV64 { unwind: false } =><= "sysv64",
172            SysV64 { unwind: true } =><= "sysv64-unwind",
173            Thiscall { unwind: false } =><= "thiscall",
174            Thiscall { unwind: true } =><= "thiscall-unwind",
175            Unadjusted =><= "unadjusted",
176            Vectorcall { unwind: false } =><= "vectorcall",
177            Vectorcall { unwind: true } =><= "vectorcall-unwind",
178            Win64 { unwind: false } =><= "win64",
179            Win64 { unwind: true } =><= "win64-unwind",
180            X86Interrupt =><= "x86-interrupt",
181    }
182}
183
184impl Ord for ExternAbi {
185    fn cmp(&self, rhs: &Self) -> Ordering {
186        self.as_str().cmp(rhs.as_str())
187    }
188}
189
190impl PartialOrd for ExternAbi {
191    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
192        Some(self.cmp(rhs))
193    }
194}
195
196impl PartialEq for ExternAbi {
197    fn eq(&self, rhs: &Self) -> bool {
198        self.cmp(rhs) == Ordering::Equal
199    }
200}
201
202impl Eq for ExternAbi {}
203
204impl Hash for ExternAbi {
205    fn hash<H: Hasher>(&self, state: &mut H) {
206        self.as_str().hash(state);
207        // double-assurance of a prefix breaker
208        u32::from_be_bytes(*b"ABI\0").hash(state);
209    }
210}
211
212#[cfg(feature = "nightly")]
213impl<C> HashStable<C> for ExternAbi {
214    #[inline]
215    fn hash_stable(&self, _: &mut C, hasher: &mut StableHasher) {
216        Hash::hash(self, hasher);
217    }
218}
219
220#[cfg(feature = "nightly")]
221impl StableOrd for ExternAbi {
222    const CAN_USE_UNSTABLE_SORT: bool = true;
223
224    // because each ABI is hashed like a string, there is no possible instability
225    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
226}
227
228#[cfg(feature = "nightly")]
229rustc_error_messages::into_diag_arg_using_display!(ExternAbi);
230
231#[cfg(feature = "nightly")]
232pub enum CVariadicStatus {
233    NotSupported,
234    Stable,
235    Unstable { feature: Symbol },
236}
237
238impl ExternAbi {
239    /// An ABI "like Rust"
240    ///
241    /// These ABIs are fully controlled by the Rust compiler, which means they
242    /// - support unwinding with `-Cpanic=unwind`, unlike `extern "C"`
243    /// - often diverge from the C ABI
244    /// - are subject to change between compiler versions
245    pub fn is_rustic_abi(self) -> bool {
246        use ExternAbi::*;
247        matches!(self, Rust | RustCall | RustCold)
248    }
249
250    /// Returns whether the ABI supports C variadics. This only controls whether we allow *imports*
251    /// of such functions via `extern` blocks; there's a separate check during AST construction
252    /// guarding *definitions* of variadic functions.
253    #[cfg(feature = "nightly")]
254    pub fn supports_c_variadic(self) -> CVariadicStatus {
255        // * C and Cdecl obviously support varargs.
256        // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.
257        // * EfiApi is based on Win64 or C, so it also supports it.
258        // * System automatically falls back to C when used with variadics, therefore supports it.
259        //
260        // * Stdcall does not, because it would be impossible for the callee to clean
261        //   up the arguments. (callee doesn't know how many arguments are there)
262        // * Same for Fastcall, Vectorcall and Thiscall.
263        // * Other calling conventions are related to hardware or the compiler itself.
264        //
265        // All of the supported ones must have a test in `tests/codegen/cffi/c-variadic-ffi.rs`.
266        match self {
267            Self::C { .. }
268            | Self::Cdecl { .. }
269            | Self::Aapcs { .. }
270            | Self::Win64 { .. }
271            | Self::SysV64 { .. }
272            | Self::EfiApi => CVariadicStatus::Stable,
273            Self::System { .. } => {
274                CVariadicStatus::Unstable { feature: rustc_span::sym::extern_system_varargs }
275            }
276            _ => CVariadicStatus::NotSupported,
277        }
278    }
279}
280
281pub fn all_names() -> Vec<&'static str> {
282    ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect()
283}
284
285impl ExternAbi {
286    /// Default ABI chosen for `extern fn` declarations without an explicit ABI.
287    pub const FALLBACK: ExternAbi = ExternAbi::C { unwind: false };
288
289    pub fn name(self) -> &'static str {
290        self.as_str()
291    }
292}
293
294impl fmt::Display for ExternAbi {
295    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296        write!(f, "\"{}\"", self.as_str())
297    }
298}