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