1use std::cmp::Ordering;2use std::fmt;3use std::hash::{Hash, Hasher};45#[cfg(feature = "nightly")]6use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher, StableOrd};7#[cfg(feature = "nightly")]8use rustc_macros::{Decodable, Encodable};9#[cfg(feature = "nightly")]10use rustc_span::Symbol;1112use crate::AbiFromStrErr;1314#[cfg(test)]15mod tests;1617/// 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 platform23 C {24 unwind: bool,25 },26 /// ABI of the "system" interface, e.g. the Win32 API, always "aliasing"27 System {28 unwind: bool,29 },3031 /// that's us!32 Rust,33 /// the mostly-unused `unboxed_closures` ABI, effectively now an impl detail unless someone34 /// 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 in37 /// `extern "Rust"` callers is worth paying extra cost in the callee.38 /// Stronger than just `#[cold]` because `fn` pointers might be incompatible.39 RustCold,4041 /// 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,4445 /// Preserves no registers.46 ///47 /// Note, that this ABI is not stable in the registers it uses, is intended as an optimization48 /// and may fall-back to a more conservative calling convention if the backend does not support49 /// forcing callers to save all registers.50 RustPreserveNone,5152 /// Ensures that calls in tail position can always be optimized into a jump.53 ///54 /// This ABI is not stable, and relies on LLVM implementation details.55 RustTail,5657 /// Unstable ABI used to call LLVM intrinsics.58 LlvmIntrinsic,5960 /// An ABI that rustc does not know how to call or define. Functions with this ABI can61 /// only be created using `#[naked]` functions or `extern "custom"` blocks, and can only62 /// be called from inline assembly.63 Custom,6465 /// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias66 /// and only valid on platforms that have a UEFI standard67 EfiApi,6869 /// Swift's calling convention, used to interoperate with Swift code without70 /// going through C as an intermediary.71 Swift,7273 /* arm */74 /// Arm Architecture Procedure Call Standard, sometimes `ExternAbi::C` is an alias for this75 Aapcs {76 unwind: bool,77 },78 /// extremely constrained barely-C ABI for TrustZone79 CmseNonSecureCall,80 /// extremely constrained barely-C ABI for TrustZone81 CmseNonSecureEntry,8283 /* gpu */84 /// An entry-point function called by the GPU's host85 GpuKernel,86 /// An entry-point function called by the GPU's host87 // FIXME: why do we have two of these?88 PtxKernel,8990 /* interrupt */91 AvrInterrupt,92 AvrNonBlockingInterrupt,93 Msp430Interrupt,94 RiscvInterruptM,95 RiscvInterruptS,96 X86Interrupt,9798 /* x86 */99 /// `ExternAbi::C` but spelled funny because x86100 Cdecl {101 unwind: bool,102 },103 /// gnu-stdcall on "unix" and win-stdcall on "windows"104 Stdcall {105 unwind: bool,106 },107 /// gnu-fastcall on "unix" and win-fastcall on "windows"108 Fastcall {109 unwind: bool,110 },111 /// windows C++ ABI112 Thiscall {113 unwind: bool,114 },115 /// uses AVX and stuff116 Vectorcall {117 unwind: bool,118 },119120 /* x86_64 */121 SysV64 {122 unwind: bool,123 },124 Win64 {125 unwind: bool,126 },127}128129macro_rules! abi_impls {130 ($e_name:ident = {131 $($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)*132 }) => {133 impl $e_name {134 pub const ALL_VARIANTS: &[Self] = &[135 $($e_name::$variant $({ unwind: $uw })*,)*136 ];137 pub const fn as_str(&self) -> &'static str {138 match self {139 $($e_name::$variant $( { unwind: $uw } )* => $tok,)*140 }141 }142 // FIXME(FnSigKind): when PartialEq is stably const, use it instead143 const fn internal_const_eq(&self, other: &Self) -> bool {144 match (self, other) {145 $( ( $e_name::$variant $( { unwind: $uw } )* , $e_name::$variant $( { unwind: $uw } )* ) => true,)*146 _ => false,147 }148 }149 // ALL_VARIANTS.iter().position(|v| v == self), but const150 pub const fn as_packed(&self) -> u8 {151 let mut index = 0;152 while index < $e_name::ALL_VARIANTS.len() {153 if self.internal_const_eq(&$e_name::ALL_VARIANTS[index]) {154 return index as u8;155 }156 index += 1;157 }158 panic!("unreachable: invalid ExternAbi variant");159 }160 pub const fn from_packed(index: u8) -> Self {161 let index = index as usize;162 assert!(index < $e_name::ALL_VARIANTS.len(), "invalid ExternAbi index");163 $e_name::ALL_VARIANTS[index]164 }165 }166167 impl ::core::str::FromStr for $e_name {168 type Err = AbiFromStrErr;169 fn from_str(s: &str) -> Result<$e_name, Self::Err> {170 match s {171 $($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*172 _ => Err(AbiFromStrErr::Unknown),173 }174 }175 }176 }177}178179abi_impls! {180 ExternAbi = {181 C { unwind: false } =><= "C",182 C { unwind: true } =><= "C-unwind",183 Rust =><= "Rust",184 Swift =><= "Swift",185 Aapcs { unwind: false } =><= "aapcs",186 Aapcs { unwind: true } =><= "aapcs-unwind",187 AvrInterrupt =><= "avr-interrupt",188 AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",189 Cdecl { unwind: false } =><= "cdecl",190 Cdecl { unwind: true } =><= "cdecl-unwind",191 CmseNonSecureCall =><= "cmse-nonsecure-call",192 CmseNonSecureEntry =><= "cmse-nonsecure-entry",193 Custom =><= "custom",194 EfiApi =><= "efiapi",195 Fastcall { unwind: false } =><= "fastcall",196 Fastcall { unwind: true } =><= "fastcall-unwind",197 GpuKernel =><= "gpu-kernel",198 LlvmIntrinsic =><= "llvm-intrinsic",199 Msp430Interrupt =><= "msp430-interrupt",200 PtxKernel =><= "ptx-kernel",201 RiscvInterruptM =><= "riscv-interrupt-m",202 RiscvInterruptS =><= "riscv-interrupt-s",203 RustCall =><= "rust-call",204 RustCold =><= "rust-cold",205 RustInvalid =><= "rust-invalid",206 RustPreserveNone =><= "rust-preserve-none",207 Stdcall { unwind: false } =><= "stdcall",208 Stdcall { unwind: true } =><= "stdcall-unwind",209 System { unwind: false } =><= "system",210 System { unwind: true } =><= "system-unwind",211 SysV64 { unwind: false } =><= "sysv64",212 SysV64 { unwind: true } =><= "sysv64-unwind",213 RustTail =><= "tail",214 Thiscall { unwind: false } =><= "thiscall",215 Thiscall { unwind: true } =><= "thiscall-unwind",216 Vectorcall { unwind: false } =><= "vectorcall",217 Vectorcall { unwind: true } =><= "vectorcall-unwind",218 Win64 { unwind: false } =><= "win64",219 Win64 { unwind: true } =><= "win64-unwind",220 X86Interrupt =><= "x86-interrupt",221 }222}223224impl Ord for ExternAbi {225 fn cmp(&self, rhs: &Self) -> Ordering {226 self.as_str().cmp(rhs.as_str())227 }228}229230impl PartialOrd for ExternAbi {231 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {232 Some(self.cmp(rhs))233 }234}235236impl PartialEq for ExternAbi {237 fn eq(&self, rhs: &Self) -> bool {238 self.cmp(rhs) == Ordering::Equal239 }240}241242impl Eq for ExternAbi {}243244impl Hash for ExternAbi {245 fn hash<H: Hasher>(&self, state: &mut H) {246 self.as_str().hash(state);247 // double-assurance of a prefix breaker248 u32::from_be_bytes(*b"ABI\0").hash(state);249 }250}251252#[cfg(feature = "nightly")]253impl StableHash for ExternAbi {254 #[inline]255 fn stable_hash<Hcx: StableHashCtxt>(&self, _: &mut Hcx, hasher: &mut StableHasher) {256 Hash::hash(self, hasher);257 }258}259260#[cfg(feature = "nightly")]261impl StableOrd for ExternAbi {262 const CAN_USE_UNSTABLE_SORT: bool = true;263264 // because each ABI is hashed like a string, there is no possible instability265 const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();266}267268#[cfg(feature = "nightly")]269rustc_error_messages::into_diag_arg_using_display!(ExternAbi);270271#[cfg(feature = "nightly")]272#[derive(Debug)]273pub enum CVariadicStatus {274 NotSupported,275 Stable,276 Unstable { feature: Symbol },277}278279impl ExternAbi {280 /// An ABI "like Rust"281 ///282 /// These ABIs are fully controlled by the Rust compiler, which means they283 /// - support unwinding with `-Cpanic=unwind`, unlike `extern "C"`284 /// - often diverge from the C ABI285 /// - are subject to change between compiler versions286 pub fn is_rustic_abi(self) -> bool {287 use ExternAbi::*;288 matches!(self, Rust | RustCall | RustCold | RustPreserveNone | RustTail)289 }290291 /// Returns whether the ABI supports C variadics. This only controls whether we allow *imports*292 /// of such functions via `extern` blocks and definition via naked functions; there's a293 /// separate check during AST construction guarding *definitions* of variadic functions.294 #[cfg(feature = "nightly")]295 pub fn supports_c_variadic(self) -> CVariadicStatus {296 // * C and Cdecl obviously support varargs.297 // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.298 // * EfiApi is based on Win64 or C, so it also supports it.299 // * System automatically falls back to C when used with variadics, therefore supports it.300 //301 // * Stdcall does not, because it would be impossible for the callee to clean302 // up the arguments. (callee doesn't know how many arguments are there)303 // * Same for Fastcall, Vectorcall and Thiscall.304 // * Other calling conventions are related to hardware or the compiler itself.305 //306 // All of the supported ones must have a test in `tests/codegen/cffi/c-variadic-ffi.rs`.307 match self {308 Self::C { .. }309 | Self::Cdecl { .. }310 | Self::Aapcs { .. }311 | Self::Win64 { .. }312 | Self::SysV64 { .. }313 | Self::EfiApi314 | Self::System { .. } => CVariadicStatus::Stable,315 _ => CVariadicStatus::NotSupported,316 }317 }318319 /// Returns whether the ABI supports guaranteed tail calls.320 #[cfg(feature = "nightly")]321 pub fn supports_guaranteed_tail_call(self) -> bool {322 match self {323 Self::CmseNonSecureCall | Self::CmseNonSecureEntry => {324 // See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers325 // before returning, and hence cannot guarantee a tail call.326 false327 }328 Self::AvrInterrupt329 | Self::AvrNonBlockingInterrupt330 | Self::Msp430Interrupt331 | Self::RiscvInterruptM332 | Self::RiscvInterruptS333 | Self::X86Interrupt => {334 // See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly.335 false336 }337 Self::GpuKernel | Self::PtxKernel => {338 // See https://godbolt.org/z/jq5TE5jK1.339 false340 }341 Self::Custom => {342 // This ABI does not support calls at all (except via assembly).343 false344 }345 Self::RustCall => {346 // Argument untupling requires additional support for tail calls to be possible,347 // see <https://github.com/rust-lang/rust/pull/158248>. There is no real uses of348 // tail calling `extern "rust-call"` functions349 false350 }351352 Self::C { .. }353 | Self::System { .. }354 | Self::Rust355 | Self::RustCold356 | Self::RustInvalid357 | Self::LlvmIntrinsic358 | Self::EfiApi359 | Self::Aapcs { .. }360 | Self::Cdecl { .. }361 | Self::Stdcall { .. }362 | Self::Fastcall { .. }363 | Self::Thiscall { .. }364 | Self::Vectorcall { .. }365 | Self::SysV64 { .. }366 | Self::Win64 { .. }367 | Self::RustPreserveNone368 | Self::RustTail369 | Self::Swift => true,370 }371 }372}373374pub fn all_names() -> Vec<&'static str> {375 ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect()376}377378impl ExternAbi {379 /// Default ABI chosen for `extern fn` declarations without an explicit ABI.380 pub const FALLBACK: ExternAbi = ExternAbi::C { unwind: false };381382 pub fn name(self) -> &'static str {383 self.as_str()384 }385}386387impl fmt::Display for ExternAbi {388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {389 write!(f, "\"{}\"", self.as_str())390 }391}