| use std::cmp::Ordering; | |
| use std::fmt; | |
| use std::hash::{Hash, Hasher}; | |
| #[cfg(feature = "nightly")] | |
| use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher, StableOrd}; | |
| #[cfg(feature = "nightly")] | |
| use rustc_macros::{Decodable, Encodable}; | |
| #[cfg(feature = "nightly")] | |
| use rustc_span::Symbol; | |
| use crate::AbiFromStrErr; | |
| #[cfg(test)] | |
| mod tests; | |
| /// ABI we expect to see within `extern "{abi}"` | |
| #[derive(Clone, Copy, Debug)] | |
| #[cfg_attr(feature = "nightly", derive(Encodable, Decodable))] | |
| pub enum ExternAbi { | |
| /* universal */ | |
| /// presumed C ABI for the platform | |
| C { | |
| unwind: bool, | |
| }, | |
| /// ABI of the "system" interface, e.g. the Win32 API, always "aliasing" | |
| System { | |
| unwind: bool, | |
| }, | |
| /// that's us! | |
| Rust, | |
| /// the mostly-unused `unboxed_closures` ABI, effectively now an impl detail unless someone | |
| /// puts in the work to make it viable again... but would we need a special ABI? | |
| RustCall, | |
| /// For things unlikely to be called, where reducing register pressure in | |
| /// `extern "Rust"` callers is worth paying extra cost in the callee. | |
| /// Stronger than just `#[cold]` because `fn` pointers might be incompatible. | |
| RustCold, | |
| /// An always-invalid ABI that's used to test "this ABI is not supported by this platform" | |
| /// in a platform-agnostic way. | |
| RustInvalid, | |
| /// Preserves no registers. | |
| /// | |
| /// Note, that this ABI is not stable in the registers it uses, is intended as an optimization | |
| /// and may fall-back to a more conservative calling convention if the backend does not support | |
| /// forcing callers to save all registers. | |
| RustPreserveNone, | |
| /// Ensures that calls in tail position can always be optimized into a jump. | |
| /// | |
| /// This ABI is not stable, and relies on LLVM implementation details. | |
| RustTail, | |
| /// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM. | |
| /// Even normally-compatible Rust types can become ABI-incompatible with this ABI! | |
| Unadjusted, | |
| /// An ABI that rustc does not know how to call or define. Functions with this ABI can | |
| /// only be created using `#[naked]` functions or `extern "custom"` blocks, and can only | |
| /// be called from inline assembly. | |
| Custom, | |
| /// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias | |
| /// and only valid on platforms that have a UEFI standard | |
| EfiApi, | |
| /// Swift's calling convention, used to interoperate with Swift code without | |
| /// going through C as an intermediary. | |
| Swift, | |
| /* arm */ | |
| /// Arm Architecture Procedure Call Standard, sometimes `ExternAbi::C` is an alias for this | |
| Aapcs { | |
| unwind: bool, | |
| }, | |
| /// extremely constrained barely-C ABI for TrustZone | |
| CmseNonSecureCall, | |
| /// extremely constrained barely-C ABI for TrustZone | |
| CmseNonSecureEntry, | |
| /* gpu */ | |
| /// An entry-point function called by the GPU's host | |
| GpuKernel, | |
| /// An entry-point function called by the GPU's host | |
| // FIXME: why do we have two of these? | |
| PtxKernel, | |
| /* interrupt */ | |
| AvrInterrupt, | |
| AvrNonBlockingInterrupt, | |
| Msp430Interrupt, | |
| RiscvInterruptM, | |
| RiscvInterruptS, | |
| X86Interrupt, | |
| /* x86 */ | |
| /// `ExternAbi::C` but spelled funny because x86 | |
| Cdecl { | |
| unwind: bool, | |
| }, | |
| /// gnu-stdcall on "unix" and win-stdcall on "windows" | |
| Stdcall { | |
| unwind: bool, | |
| }, | |
| /// gnu-fastcall on "unix" and win-fastcall on "windows" | |
| Fastcall { | |
| unwind: bool, | |
| }, | |
| /// windows C++ ABI | |
| Thiscall { | |
| unwind: bool, | |
| }, | |
| /// uses AVX and stuff | |
| Vectorcall { | |
| unwind: bool, | |
| }, | |
| /* x86_64 */ | |
| SysV64 { | |
| unwind: bool, | |
| }, | |
| Win64 { | |
| unwind: bool, | |
| }, | |
| } | |
| macro_rules! abi_impls { | |
| ($e_name:ident = { | |
| $($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)* | |
| }) => { | |
| impl $e_name { | |
| pub const ALL_VARIANTS: &[Self] = &[ | |
| $($e_name::$variant $({ unwind: $uw })*,)* | |
| ]; | |
| pub const fn as_str(&self) -> &'static str { | |
| match self { | |
| $($e_name::$variant $( { unwind: $uw } )* => $tok,)* | |
| } | |
| } | |
| // FIXME(FnSigKind): when PartialEq is stably const, use it instead | |
| const fn internal_const_eq(&self, other: &Self) -> bool { | |
| match (self, other) { | |
| $( ( $e_name::$variant $( { unwind: $uw } )* , $e_name::$variant $( { unwind: $uw } )* ) => true,)* | |
| _ => false, | |
| } | |
| } | |
| // ALL_VARIANTS.iter().position(|v| v == self), but const | |
| pub const fn as_packed(&self) -> u8 { | |
| let mut index = 0; | |
| while index < $e_name::ALL_VARIANTS.len() { | |
| if self.internal_const_eq(&$e_name::ALL_VARIANTS[index]) { | |
| return index as u8; | |
| } | |
| index += 1; | |
| } | |
| panic!("unreachable: invalid ExternAbi variant"); | |
| } | |
| pub const fn from_packed(index: u8) -> Self { | |
| let index = index as usize; | |
| assert!(index < $e_name::ALL_VARIANTS.len(), "invalid ExternAbi index"); | |
| $e_name::ALL_VARIANTS[index] | |
| } | |
| } | |
| impl ::core::str::FromStr for $e_name { | |
| type Err = AbiFromStrErr; | |
| fn from_str(s: &str) -> Result<$e_name, Self::Err> { | |
| match s { | |
| $($tok => Ok($e_name::$variant $({ unwind: $uw })*),)* | |
| _ => Err(AbiFromStrErr::Unknown), | |
| } | |
| } | |
| } | |
| } | |
| } | |
| abi_impls! { | |
| ExternAbi = { | |
| C { unwind: false } =><= "C", | |
| C { unwind: true } =><= "C-unwind", | |
| Rust =><= "Rust", | |
| Swift =><= "Swift", | |
| Aapcs { unwind: false } =><= "aapcs", | |
| Aapcs { unwind: true } =><= "aapcs-unwind", | |
| AvrInterrupt =><= "avr-interrupt", | |
| AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt", | |
| Cdecl { unwind: false } =><= "cdecl", | |
| Cdecl { unwind: true } =><= "cdecl-unwind", | |
| CmseNonSecureCall =><= "cmse-nonsecure-call", | |
| CmseNonSecureEntry =><= "cmse-nonsecure-entry", | |
| Custom =><= "custom", | |
| EfiApi =><= "efiapi", | |
| Fastcall { unwind: false } =><= "fastcall", | |
| Fastcall { unwind: true } =><= "fastcall-unwind", | |
| GpuKernel =><= "gpu-kernel", | |
| Msp430Interrupt =><= "msp430-interrupt", | |
| PtxKernel =><= "ptx-kernel", | |
| RiscvInterruptM =><= "riscv-interrupt-m", | |
| RiscvInterruptS =><= "riscv-interrupt-s", | |
| RustCall =><= "rust-call", | |
| RustCold =><= "rust-cold", | |
| RustInvalid =><= "rust-invalid", | |
| RustPreserveNone =><= "rust-preserve-none", | |
| Stdcall { unwind: false } =><= "stdcall", | |
| Stdcall { unwind: true } =><= "stdcall-unwind", | |
| System { unwind: false } =><= "system", | |
| System { unwind: true } =><= "system-unwind", | |
| SysV64 { unwind: false } =><= "sysv64", | |
| SysV64 { unwind: true } =><= "sysv64-unwind", | |
| RustTail =><= "tail", | |
| Thiscall { unwind: false } =><= "thiscall", | |
| Thiscall { unwind: true } =><= "thiscall-unwind", | |
| Unadjusted =><= "unadjusted", | |
| Vectorcall { unwind: false } =><= "vectorcall", | |
| Vectorcall { unwind: true } =><= "vectorcall-unwind", | |
| Win64 { unwind: false } =><= "win64", | |
| Win64 { unwind: true } =><= "win64-unwind", | |
| X86Interrupt =><= "x86-interrupt", | |
| } | |
| } | |
| impl Ord for ExternAbi { | |
| fn cmp(&self, rhs: &Self) -> Ordering { | |
| self.as_str().cmp(rhs.as_str()) | |
| } | |
| } | |
| impl PartialOrd for ExternAbi { | |
| fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> { | |
| Some(self.cmp(rhs)) | |
| } | |
| } | |
| impl PartialEq for ExternAbi { | |
| fn eq(&self, rhs: &Self) -> bool { | |
| self.cmp(rhs) == Ordering::Equal | |
| } | |
| } | |
| impl Eq for ExternAbi {} | |
| impl Hash for ExternAbi { | |
| fn hash<H: Hasher>(&self, state: &mut H) { | |
| self.as_str().hash(state); | |
| // double-assurance of a prefix breaker | |
| u32::from_be_bytes(*b"ABI\0").hash(state); | |
| } | |
| } | |
| #[cfg(feature = "nightly")] | |
| impl StableHash for ExternAbi { | |
| #[inline] | |
| fn stable_hash<Hcx: StableHashCtxt>(&self, _: &mut Hcx, hasher: &mut StableHasher) { | |
| Hash::hash(self, hasher); | |
| } | |
| } | |
| #[cfg(feature = "nightly")] | |
| impl StableOrd for ExternAbi { | |
| const CAN_USE_UNSTABLE_SORT: bool = true; | |
| // because each ABI is hashed like a string, there is no possible instability | |
| const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = (); | |
| } | |
| #[cfg(feature = "nightly")] | |
| rustc_error_messages::into_diag_arg_using_display!(ExternAbi); | |
| #[cfg(feature = "nightly")] | |
| #[derive(Debug)] | |
| pub enum CVariadicStatus { | |
| NotSupported, | |
| Stable, | |
| Unstable { feature: Symbol }, | |
| } | |
| impl ExternAbi { | |
| /// An ABI "like Rust" | |
| /// | |
| /// These ABIs are fully controlled by the Rust compiler, which means they | |
| /// - support unwinding with `-Cpanic=unwind`, unlike `extern "C"` | |
| /// - often diverge from the C ABI | |
| /// - are subject to change between compiler versions | |
| pub fn is_rustic_abi(self) -> bool { | |
| use ExternAbi::*; | |
| matches!(self, Rust | RustCall | RustCold | RustPreserveNone | RustTail) | |
| } | |
| /// Returns whether the ABI supports C variadics. This only controls whether we allow *imports* | |
| /// of such functions via `extern` blocks; there's a separate check during AST construction | |
| /// guarding *definitions* of variadic functions. | |
| #[cfg(feature = "nightly")] | |
| pub fn supports_c_variadic(self) -> CVariadicStatus { | |
| // * C and Cdecl obviously support varargs. | |
| // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs. | |
| // * EfiApi is based on Win64 or C, so it also supports it. | |
| // * System automatically falls back to C when used with variadics, therefore supports it. | |
| // | |
| // * Stdcall does not, because it would be impossible for the callee to clean | |
| // up the arguments. (callee doesn't know how many arguments are there) | |
| // * Same for Fastcall, Vectorcall and Thiscall. | |
| // * Other calling conventions are related to hardware or the compiler itself. | |
| // | |
| // All of the supported ones must have a test in `tests/codegen/cffi/c-variadic-ffi.rs`. | |
| match self { | |
| Self::C { .. } | |
| | Self::Cdecl { .. } | |
| | Self::Aapcs { .. } | |
| | Self::Win64 { .. } | |
| | Self::SysV64 { .. } | |
| | Self::EfiApi | |
| | Self::System { .. } => CVariadicStatus::Stable, | |
| _ => CVariadicStatus::NotSupported, | |
| } | |
| } | |
| /// Returns whether the ABI supports guaranteed tail calls. | |
| #[cfg(feature = "nightly")] | |
| pub fn supports_guaranteed_tail_call(self) -> bool { | |
| match self { | |
| Self::CmseNonSecureCall | Self::CmseNonSecureEntry => { | |
| // See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers | |
| // before returning, and hence cannot guarantee a tail call. | |
| false | |
| } | |
| Self::AvrInterrupt | |
| | Self::AvrNonBlockingInterrupt | |
| | Self::Msp430Interrupt | |
| | Self::RiscvInterruptM | |
| | Self::RiscvInterruptS | |
| | Self::X86Interrupt => { | |
| // See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly. | |
| false | |
| } | |
| Self::GpuKernel | Self::PtxKernel => { | |
| // See https://godbolt.org/z/jq5TE5jK1. | |
| false | |
| } | |
| Self::Custom => { | |
| // This ABI does not support calls at all (except via assembly). | |
| false | |
| } | |
| Self::RustCall => { | |
| // Argument untupling requires additional support for tail calls to be possible, | |
| // see <https://github.com/rust-lang/rust/pull/158248>. There is no real uses of | |
| // tail calling `extern "rust-call"` functions | |
| false | |
| } | |
| Self::C { .. } | |
| | Self::System { .. } | |
| | Self::Rust | |
| | Self::RustCold | |
| | Self::RustInvalid | |
| | Self::Unadjusted | |
| | Self::EfiApi | |
| | Self::Aapcs { .. } | |
| | Self::Cdecl { .. } | |
| | Self::Stdcall { .. } | |
| | Self::Fastcall { .. } | |
| | Self::Thiscall { .. } | |
| | Self::Vectorcall { .. } | |
| | Self::SysV64 { .. } | |
| | Self::Win64 { .. } | |
| | Self::RustPreserveNone | |
| | Self::RustTail | |
| | Self::Swift => true, | |
| } | |
| } | |
| } | |
| pub fn all_names() -> Vec<&'static str> { | |
| ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect() | |
| } | |
| impl ExternAbi { | |
| /// Default ABI chosen for `extern fn` declarations without an explicit ABI. | |
| pub const FALLBACK: ExternAbi = ExternAbi::C { unwind: false }; | |
| pub fn name(self) -> &'static str { | |
| self.as_str() | |
| } | |
| } | |
| impl fmt::Display for ExternAbi { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| write!(f, "\"{}\"", self.as_str()) | |
| } | |
| } |