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