1use std::cmp::Ordering;2use std::fmt;3use std::hash::{Hash, Hasher};45#[cfg(feature = "nightly")]6use rustc_data_structures::stable_hasher::{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 /// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM.53 /// Even normally-compatible Rust types can become ABI-incompatible with this ABI!54 Unadjusted,5556 /// An ABI that rustc does not know how to call or define. Functions with this ABI can57 /// only be created using `#[naked]` functions or `extern "custom"` blocks, and can only58 /// be called from inline assembly.59 Custom,6061 /// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias62 /// and only valid on platforms that have a UEFI standard63 EfiApi,6465 /* arm */66 /// Arm Architecture Procedure Call Standard, sometimes `ExternAbi::C` is an alias for this67 Aapcs {68 unwind: bool,69 },70 /// extremely constrained barely-C ABI for TrustZone71 CmseNonSecureCall,72 /// extremely constrained barely-C ABI for TrustZone73 CmseNonSecureEntry,7475 /* gpu */76 /// An entry-point function called by the GPU's host77 GpuKernel,78 /// An entry-point function called by the GPU's host79 // FIXME: why do we have two of these?80 PtxKernel,8182 /* interrupt */83 AvrInterrupt,84 AvrNonBlockingInterrupt,85 Msp430Interrupt,86 RiscvInterruptM,87 RiscvInterruptS,88 X86Interrupt,8990 /* x86 */91 /// `ExternAbi::C` but spelled funny because x8692 Cdecl {93 unwind: bool,94 },95 /// gnu-stdcall on "unix" and win-stdcall on "windows"96 Stdcall {97 unwind: bool,98 },99 /// gnu-fastcall on "unix" and win-fastcall on "windows"100 Fastcall {101 unwind: bool,102 },103 /// windows C++ ABI104 Thiscall {105 unwind: bool,106 },107 /// uses AVX and stuff108 Vectorcall {109 unwind: bool,110 },111112 /* x86_64 */113 SysV64 {114 unwind: bool,115 },116 Win64 {117 unwind: bool,118 },119}120121macro_rules! abi_impls {122 ($e_name:ident = {123 $($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)*124 }) => {125 impl $e_name {126 pub const ALL_VARIANTS: &[Self] = &[127 $($e_name::$variant $({ unwind: $uw })*,)*128 ];129 pub const fn as_str(&self) -> &'static str {130 match self {131 $($e_name::$variant $( { unwind: $uw } )* => $tok,)*132 }133 }134 // FIXME(FnSigKind): when PartialEq is stably const, use it instead135 const fn internal_const_eq(&self, other: &Self) -> bool {136 match (self, other) {137 $( ( $e_name::$variant $( { unwind: $uw } )* , $e_name::$variant $( { unwind: $uw } )* ) => true,)*138 _ => false,139 }140 }141 // ALL_VARIANTS.iter().position(|v| v == self), but const142 pub const fn as_packed(&self) -> u8 {143 let mut index = 0;144 while index < $e_name::ALL_VARIANTS.len() {145 if self.internal_const_eq(&$e_name::ALL_VARIANTS[index]) {146 return index as u8;147 }148 index += 1;149 }150 panic!("unreachable: invalid ExternAbi variant");151 }152 pub const fn from_packed(index: u8) -> Self {153 let index = index as usize;154 assert!(index < $e_name::ALL_VARIANTS.len(), "invalid ExternAbi index");155 $e_name::ALL_VARIANTS[index]156 }157 }158159 impl ::core::str::FromStr for $e_name {160 type Err = AbiFromStrErr;161 fn from_str(s: &str) -> Result<$e_name, Self::Err> {162 match s {163 $($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*164 _ => Err(AbiFromStrErr::Unknown),165 }166 }167 }168 }169}170171abi_impls! {172 ExternAbi = {173 C { unwind: false } =><= "C",174 C { unwind: true } =><= "C-unwind",175 Rust =><= "Rust",176 Aapcs { unwind: false } =><= "aapcs",177 Aapcs { unwind: true } =><= "aapcs-unwind",178 AvrInterrupt =><= "avr-interrupt",179 AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",180 Cdecl { unwind: false } =><= "cdecl",181 Cdecl { unwind: true } =><= "cdecl-unwind",182 CmseNonSecureCall =><= "cmse-nonsecure-call",183 CmseNonSecureEntry =><= "cmse-nonsecure-entry",184 Custom =><= "custom",185 EfiApi =><= "efiapi",186 Fastcall { unwind: false } =><= "fastcall",187 Fastcall { unwind: true } =><= "fastcall-unwind",188 GpuKernel =><= "gpu-kernel",189 Msp430Interrupt =><= "msp430-interrupt",190 PtxKernel =><= "ptx-kernel",191 RiscvInterruptM =><= "riscv-interrupt-m",192 RiscvInterruptS =><= "riscv-interrupt-s",193 RustCall =><= "rust-call",194 RustCold =><= "rust-cold",195 RustInvalid =><= "rust-invalid",196 RustPreserveNone =><= "rust-preserve-none",197 Stdcall { unwind: false } =><= "stdcall",198 Stdcall { unwind: true } =><= "stdcall-unwind",199 System { unwind: false } =><= "system",200 System { unwind: true } =><= "system-unwind",201 SysV64 { unwind: false } =><= "sysv64",202 SysV64 { unwind: true } =><= "sysv64-unwind",203 Thiscall { unwind: false } =><= "thiscall",204 Thiscall { unwind: true } =><= "thiscall-unwind",205 Unadjusted =><= "unadjusted",206 Vectorcall { unwind: false } =><= "vectorcall",207 Vectorcall { unwind: true } =><= "vectorcall-unwind",208 Win64 { unwind: false } =><= "win64",209 Win64 { unwind: true } =><= "win64-unwind",210 X86Interrupt =><= "x86-interrupt",211 }212}213214impl Ord for ExternAbi {215 fn cmp(&self, rhs: &Self) -> Ordering {216 self.as_str().cmp(rhs.as_str())217 }218}219220impl PartialOrd for ExternAbi {221 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {222 Some(self.cmp(rhs))223 }224}225226impl PartialEq for ExternAbi {227 fn eq(&self, rhs: &Self) -> bool {228 self.cmp(rhs) == Ordering::Equal229 }230}231232impl Eq for ExternAbi {}233234impl Hash for ExternAbi {235 fn hash<H: Hasher>(&self, state: &mut H) {236 self.as_str().hash(state);237 // double-assurance of a prefix breaker238 u32::from_be_bytes(*b"ABI\0").hash(state);239 }240}241242#[cfg(feature = "nightly")]243impl StableHash for ExternAbi {244 #[inline]245 fn stable_hash<Hcx: StableHashCtxt>(&self, _: &mut Hcx, hasher: &mut StableHasher) {246 Hash::hash(self, hasher);247 }248}249250#[cfg(feature = "nightly")]251impl StableOrd for ExternAbi {252 const CAN_USE_UNSTABLE_SORT: bool = true;253254 // because each ABI is hashed like a string, there is no possible instability255 const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();256}257258#[cfg(feature = "nightly")]259rustc_error_messages::into_diag_arg_using_display!(ExternAbi);260261#[cfg(feature = "nightly")]262#[derive(Debug)]263pub enum CVariadicStatus {264 NotSupported,265 Stable,266 Unstable { feature: Symbol },267}268269impl ExternAbi {270 /// An ABI "like Rust"271 ///272 /// These ABIs are fully controlled by the Rust compiler, which means they273 /// - support unwinding with `-Cpanic=unwind`, unlike `extern "C"`274 /// - often diverge from the C ABI275 /// - are subject to change between compiler versions276 pub fn is_rustic_abi(self) -> bool {277 use ExternAbi::*;278 matches!(self, Rust | RustCall | RustCold | RustPreserveNone)279 }280281 /// Returns whether the ABI supports C variadics. This only controls whether we allow *imports*282 /// of such functions via `extern` blocks; there's a separate check during AST construction283 /// guarding *definitions* of variadic functions.284 #[cfg(feature = "nightly")]285 pub fn supports_c_variadic(self) -> CVariadicStatus {286 // * C and Cdecl obviously support varargs.287 // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.288 // * EfiApi is based on Win64 or C, so it also supports it.289 // * System automatically falls back to C when used with variadics, therefore supports it.290 //291 // * Stdcall does not, because it would be impossible for the callee to clean292 // up the arguments. (callee doesn't know how many arguments are there)293 // * Same for Fastcall, Vectorcall and Thiscall.294 // * Other calling conventions are related to hardware or the compiler itself.295 //296 // All of the supported ones must have a test in `tests/codegen/cffi/c-variadic-ffi.rs`.297 match self {298 Self::C { .. }299 | Self::Cdecl { .. }300 | Self::Aapcs { .. }301 | Self::Win64 { .. }302 | Self::SysV64 { .. }303 | Self::EfiApi304 | Self::System { .. } => CVariadicStatus::Stable,305 _ => CVariadicStatus::NotSupported,306 }307 }308309 /// Returns whether the ABI supports guaranteed tail calls.310 #[cfg(feature = "nightly")]311 pub fn supports_guaranteed_tail_call(self) -> bool {312 match self {313 Self::CmseNonSecureCall | Self::CmseNonSecureEntry => {314 // See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers315 // before returning, and hence cannot guarantee a tail call.316 false317 }318 Self::AvrInterrupt319 | Self::AvrNonBlockingInterrupt320 | Self::Msp430Interrupt321 | Self::RiscvInterruptM322 | Self::RiscvInterruptS323 | Self::X86Interrupt => {324 // See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly.325 false326 }327 Self::GpuKernel | Self::PtxKernel => {328 // See https://godbolt.org/z/jq5TE5jK1.329 false330 }331 Self::Custom => {332 // This ABI does not support calls at all (except via assembly).333 false334 }335 Self::C { .. }336 | Self::System { .. }337 | Self::Rust338 | Self::RustCall339 | Self::RustCold340 | Self::RustInvalid341 | Self::Unadjusted342 | Self::EfiApi343 | Self::Aapcs { .. }344 | Self::Cdecl { .. }345 | Self::Stdcall { .. }346 | Self::Fastcall { .. }347 | Self::Thiscall { .. }348 | Self::Vectorcall { .. }349 | Self::SysV64 { .. }350 | Self::Win64 { .. }351 | Self::RustPreserveNone => true,352 }353 }354}355356pub fn all_names() -> Vec<&'static str> {357 ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect()358}359360impl ExternAbi {361 /// Default ABI chosen for `extern fn` declarations without an explicit ABI.362 pub const FALLBACK: ExternAbi = ExternAbi::C { unwind: false };363364 pub fn name(self) -> &'static str {365 self.as_str()366 }367}368369impl fmt::Display for ExternAbi {370 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {371 write!(f, "\"{}\"", self.as_str())372 }373}