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
15use ExternAbi as Abi;
16
17#[derive(Clone, Copy, Debug)]
18#[cfg_attr(feature = "nightly", derive(Encodable, Decodable))]
19pub enum ExternAbi {
20    // Some of the ABIs come first because every time we add a new ABI, we have to re-bless all the
21    // hashing tests. These are used in many places, so giving them stable values reduces test
22    // churn. The specific values are meaningless.
23    Rust,
24    C {
25        unwind: bool,
26    },
27    Cdecl {
28        unwind: bool,
29    },
30    Stdcall {
31        unwind: bool,
32    },
33    Fastcall {
34        unwind: bool,
35    },
36    Vectorcall {
37        unwind: bool,
38    },
39    Thiscall {
40        unwind: bool,
41    },
42    Aapcs {
43        unwind: bool,
44    },
45    Win64 {
46        unwind: bool,
47    },
48    SysV64 {
49        unwind: bool,
50    },
51    PtxKernel,
52    Msp430Interrupt,
53    X86Interrupt,
54    /// An entry-point function called by the GPU's host
55    // FIXME: should not be callable from Rust on GPU targets, is for host's use only
56    GpuKernel,
57    EfiApi,
58    AvrInterrupt,
59    AvrNonBlockingInterrupt,
60    CCmseNonSecureCall,
61    CCmseNonSecureEntry,
62    System {
63        unwind: bool,
64    },
65    RustCall,
66    /// *Not* a stable ABI, just directly use the Rust types to describe the ABI for LLVM. Even
67    /// normally ABI-compatible Rust types can become ABI-incompatible with this ABI!
68    Unadjusted,
69    /// For things unlikely to be called, where reducing register pressure in
70    /// `extern "Rust"` callers is worth paying extra cost in the callee.
71    /// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
72    RustCold,
73    RiscvInterruptM,
74    RiscvInterruptS,
75}
76
77macro_rules! abi_impls {
78    ($e_name:ident = {
79        $($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)*
80    }) => {
81        impl $e_name {
82            pub const ALL_VARIANTS: &[Self] = &[
83                $($e_name::$variant $({ unwind: $uw })*,)*
84            ];
85            pub const fn as_str(&self) -> &'static str {
86                match self {
87                    $($e_name::$variant $( { unwind: $uw } )* => $tok,)*
88                }
89            }
90        }
91
92        impl ::core::str::FromStr for $e_name {
93            type Err = AbiFromStrErr;
94            fn from_str(s: &str) -> Result<$e_name, Self::Err> {
95                match s {
96                    $($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*
97                    _ => Err(AbiFromStrErr::Unknown),
98                }
99            }
100        }
101    }
102}
103
104abi_impls! {
105    ExternAbi = {
106            C { unwind: false } =><= "C",
107            CCmseNonSecureCall =><= "C-cmse-nonsecure-call",
108            CCmseNonSecureEntry =><= "C-cmse-nonsecure-entry",
109            C { unwind: true } =><= "C-unwind",
110            Rust =><= "Rust",
111            Aapcs { unwind: false } =><= "aapcs",
112            Aapcs { unwind: true } =><= "aapcs-unwind",
113            AvrInterrupt =><= "avr-interrupt",
114            AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",
115            Cdecl { unwind: false } =><= "cdecl",
116            Cdecl { unwind: true } =><= "cdecl-unwind",
117            EfiApi =><= "efiapi",
118            Fastcall { unwind: false } =><= "fastcall",
119            Fastcall { unwind: true } =><= "fastcall-unwind",
120            GpuKernel =><= "gpu-kernel",
121            Msp430Interrupt =><= "msp430-interrupt",
122            PtxKernel =><= "ptx-kernel",
123            RiscvInterruptM =><= "riscv-interrupt-m",
124            RiscvInterruptS =><= "riscv-interrupt-s",
125            RustCall =><= "rust-call",
126            RustCold =><= "rust-cold",
127            Stdcall { unwind: false } =><= "stdcall",
128            Stdcall { unwind: true } =><= "stdcall-unwind",
129            System { unwind: false } =><= "system",
130            System { unwind: true } =><= "system-unwind",
131            SysV64 { unwind: false } =><= "sysv64",
132            SysV64 { unwind: true } =><= "sysv64-unwind",
133            Thiscall { unwind: false } =><= "thiscall",
134            Thiscall { unwind: true } =><= "thiscall-unwind",
135            Unadjusted =><= "unadjusted",
136            Vectorcall { unwind: false } =><= "vectorcall",
137            Vectorcall { unwind: true } =><= "vectorcall-unwind",
138            Win64 { unwind: false } =><= "win64",
139            Win64 { unwind: true } =><= "win64-unwind",
140            X86Interrupt =><= "x86-interrupt",
141    }
142}
143
144impl Ord for ExternAbi {
145    fn cmp(&self, rhs: &Self) -> Ordering {
146        self.as_str().cmp(rhs.as_str())
147    }
148}
149
150impl PartialOrd for ExternAbi {
151    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
152        Some(self.cmp(rhs))
153    }
154}
155
156impl PartialEq for ExternAbi {
157    fn eq(&self, rhs: &Self) -> bool {
158        self.cmp(rhs) == Ordering::Equal
159    }
160}
161
162impl Eq for ExternAbi {}
163
164impl Hash for ExternAbi {
165    fn hash<H: Hasher>(&self, state: &mut H) {
166        self.as_str().hash(state);
167        // double-assurance of a prefix breaker
168        u32::from_be_bytes(*b"ABI\0").hash(state);
169    }
170}
171
172#[cfg(feature = "nightly")]
173impl<C> HashStable<C> for ExternAbi {
174    #[inline]
175    fn hash_stable(&self, _: &mut C, hasher: &mut StableHasher) {
176        Hash::hash(self, hasher);
177    }
178}
179
180#[cfg(feature = "nightly")]
181impl StableOrd for ExternAbi {
182    const CAN_USE_UNSTABLE_SORT: bool = true;
183
184    // because each ABI is hashed like a string, there is no possible instability
185    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
186}
187
188impl ExternAbi {
189    /// An ABI "like Rust"
190    ///
191    /// These ABIs are fully controlled by the Rust compiler, which means they
192    /// - support unwinding with `-Cpanic=unwind`, unlike `extern "C"`
193    /// - often diverge from the C ABI
194    /// - are subject to change between compiler versions
195    pub fn is_rustic_abi(self) -> bool {
196        use ExternAbi::*;
197        matches!(self, Rust | RustCall | RustCold)
198    }
199
200    pub fn supports_varargs(self) -> bool {
201        // * C and Cdecl obviously support varargs.
202        // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.
203        // * EfiApi is based on Win64 or C, so it also supports it.
204        //
205        // * Stdcall does not, because it would be impossible for the callee to clean
206        //   up the arguments. (callee doesn't know how many arguments are there)
207        // * Same for Fastcall, Vectorcall and Thiscall.
208        // * Other calling conventions are related to hardware or the compiler itself.
209        match self {
210            Self::C { .. }
211            | Self::Cdecl { .. }
212            | Self::Aapcs { .. }
213            | Self::Win64 { .. }
214            | Self::SysV64 { .. }
215            | Self::EfiApi => true,
216            _ => false,
217        }
218    }
219}
220
221pub fn all_names() -> Vec<&'static str> {
222    ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect()
223}
224
225impl ExternAbi {
226    /// Default ABI chosen for `extern fn` declarations without an explicit ABI.
227    pub const FALLBACK: Abi = Abi::C { unwind: false };
228
229    pub fn name(self) -> &'static str {
230        self.as_str()
231    }
232}
233
234impl fmt::Display for ExternAbi {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        write!(f, "\"{}\"", self.as_str())
237    }
238}