compiler/rustc_abi/src/extern_abi.rs RUST 387 lines View on github.com → Search inside
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 impl detail that directly uses Rust types to describe the ABI to LLVM.58    /// Even normally-compatible Rust types can become ABI-incompatible with this ABI!59    Unadjusted,6061    /// An ABI that rustc does not know how to call or define. Functions with this ABI can62    /// only be created using `#[naked]` functions or `extern "custom"` blocks, and can only63    /// be called from inline assembly.64    Custom,6566    /// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias67    /// and only valid on platforms that have a UEFI standard68    EfiApi,6970    /// Swift's calling convention, used to interoperate with Swift code without71    /// going through C as an intermediary.72    Swift,7374    /* arm */75    /// Arm Architecture Procedure Call Standard, sometimes `ExternAbi::C` is an alias for this76    Aapcs {77        unwind: bool,78    },79    /// extremely constrained barely-C ABI for TrustZone80    CmseNonSecureCall,81    /// extremely constrained barely-C ABI for TrustZone82    CmseNonSecureEntry,8384    /* gpu */85    /// An entry-point function called by the GPU's host86    GpuKernel,87    /// An entry-point function called by the GPU's host88    // FIXME: why do we have two of these?89    PtxKernel,9091    /* interrupt */92    AvrInterrupt,93    AvrNonBlockingInterrupt,94    Msp430Interrupt,95    RiscvInterruptM,96    RiscvInterruptS,97    X86Interrupt,9899    /* x86 */100    /// `ExternAbi::C` but spelled funny because x86101    Cdecl {102        unwind: bool,103    },104    /// gnu-stdcall on "unix" and win-stdcall on "windows"105    Stdcall {106        unwind: bool,107    },108    /// gnu-fastcall on "unix" and win-fastcall on "windows"109    Fastcall {110        unwind: bool,111    },112    /// windows C++ ABI113    Thiscall {114        unwind: bool,115    },116    /// uses AVX and stuff117    Vectorcall {118        unwind: bool,119    },120121    /* x86_64 */122    SysV64 {123        unwind: bool,124    },125    Win64 {126        unwind: bool,127    },128}129130macro_rules! abi_impls {131    ($e_name:ident = {132        $($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)*133    }) => {134        impl $e_name {135            pub const ALL_VARIANTS: &[Self] = &[136                $($e_name::$variant $({ unwind: $uw })*,)*137            ];138            pub const fn as_str(&self) -> &'static str {139                match self {140                    $($e_name::$variant $( { unwind: $uw } )* => $tok,)*141                }142            }143            // FIXME(FnSigKind): when PartialEq is stably const, use it instead144            const fn internal_const_eq(&self, other: &Self) -> bool {145                match (self, other) {146                    $( ( $e_name::$variant $( { unwind: $uw } )* , $e_name::$variant $( { unwind: $uw } )* ) => true,)*147                    _ => false,148                }149            }150            // ALL_VARIANTS.iter().position(|v| v == self), but const151            pub const fn as_packed(&self) -> u8 {152                let mut index = 0;153                while index < $e_name::ALL_VARIANTS.len() {154                    if self.internal_const_eq(&$e_name::ALL_VARIANTS[index]) {155                        return index as u8;156                    }157                    index += 1;158                }159                panic!("unreachable: invalid ExternAbi variant");160            }161            pub const fn from_packed(index: u8) -> Self {162                let index = index as usize;163                assert!(index < $e_name::ALL_VARIANTS.len(), "invalid ExternAbi index");164                $e_name::ALL_VARIANTS[index]165            }166        }167168        impl ::core::str::FromStr for $e_name {169            type Err = AbiFromStrErr;170            fn from_str(s: &str) -> Result<$e_name, Self::Err> {171                match s {172                    $($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*173                    _ => Err(AbiFromStrErr::Unknown),174                }175            }176        }177    }178}179180abi_impls! {181    ExternAbi = {182            C { unwind: false } =><= "C",183            C { unwind: true } =><= "C-unwind",184            Rust =><= "Rust",185            Swift =><= "Swift",186            Aapcs { unwind: false } =><= "aapcs",187            Aapcs { unwind: true } =><= "aapcs-unwind",188            AvrInterrupt =><= "avr-interrupt",189            AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",190            Cdecl { unwind: false } =><= "cdecl",191            Cdecl { unwind: true } =><= "cdecl-unwind",192            CmseNonSecureCall =><= "cmse-nonsecure-call",193            CmseNonSecureEntry =><= "cmse-nonsecure-entry",194            Custom =><= "custom",195            EfiApi =><= "efiapi",196            Fastcall { unwind: false } =><= "fastcall",197            Fastcall { unwind: true } =><= "fastcall-unwind",198            GpuKernel =><= "gpu-kernel",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            Unadjusted =><= "unadjusted",217            Vectorcall { unwind: false } =><= "vectorcall",218            Vectorcall { unwind: true } =><= "vectorcall-unwind",219            Win64 { unwind: false } =><= "win64",220            Win64 { unwind: true } =><= "win64-unwind",221            X86Interrupt =><= "x86-interrupt",222    }223}224225impl Ord for ExternAbi {226    fn cmp(&self, rhs: &Self) -> Ordering {227        self.as_str().cmp(rhs.as_str())228    }229}230231impl PartialOrd for ExternAbi {232    fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {233        Some(self.cmp(rhs))234    }235}236237impl PartialEq for ExternAbi {238    fn eq(&self, rhs: &Self) -> bool {239        self.cmp(rhs) == Ordering::Equal240    }241}242243impl Eq for ExternAbi {}244245impl Hash for ExternAbi {246    fn hash<H: Hasher>(&self, state: &mut H) {247        self.as_str().hash(state);248        // double-assurance of a prefix breaker249        u32::from_be_bytes(*b"ABI\0").hash(state);250    }251}252253#[cfg(feature = "nightly")]254impl StableHash for ExternAbi {255    #[inline]256    fn stable_hash<Hcx: StableHashCtxt>(&self, _: &mut Hcx, hasher: &mut StableHasher) {257        Hash::hash(self, hasher);258    }259}260261#[cfg(feature = "nightly")]262impl StableOrd for ExternAbi {263    const CAN_USE_UNSTABLE_SORT: bool = true;264265    // because each ABI is hashed like a string, there is no possible instability266    const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();267}268269#[cfg(feature = "nightly")]270rustc_error_messages::into_diag_arg_using_display!(ExternAbi);271272#[cfg(feature = "nightly")]273#[derive(Debug)]274pub enum CVariadicStatus {275    NotSupported,276    Stable,277    Unstable { feature: Symbol },278}279280impl ExternAbi {281    /// An ABI "like Rust"282    ///283    /// These ABIs are fully controlled by the Rust compiler, which means they284    /// - support unwinding with `-Cpanic=unwind`, unlike `extern "C"`285    /// - often diverge from the C ABI286    /// - are subject to change between compiler versions287    pub fn is_rustic_abi(self) -> bool {288        use ExternAbi::*;289        matches!(self, Rust | RustCall | RustCold | RustPreserveNone | RustTail)290    }291292    /// Returns whether the ABI supports C variadics. This only controls whether we allow *imports*293    /// of such functions via `extern` blocks; there's a separate check during AST construction294    /// guarding *definitions* of variadic functions.295    #[cfg(feature = "nightly")]296    pub fn supports_c_variadic(self) -> CVariadicStatus {297        // * C and Cdecl obviously support varargs.298        // * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.299        // * EfiApi is based on Win64 or C, so it also supports it.300        // * System automatically falls back to C when used with variadics, therefore supports it.301        //302        // * Stdcall does not, because it would be impossible for the callee to clean303        //   up the arguments. (callee doesn't know how many arguments are there)304        // * Same for Fastcall, Vectorcall and Thiscall.305        // * Other calling conventions are related to hardware or the compiler itself.306        //307        // All of the supported ones must have a test in `tests/codegen/cffi/c-variadic-ffi.rs`.308        match self {309            Self::C { .. }310            | Self::Cdecl { .. }311            | Self::Aapcs { .. }312            | Self::Win64 { .. }313            | Self::SysV64 { .. }314            | Self::EfiApi315            | Self::System { .. } => CVariadicStatus::Stable,316            _ => CVariadicStatus::NotSupported,317        }318    }319320    /// Returns whether the ABI supports guaranteed tail calls.321    #[cfg(feature = "nightly")]322    pub fn supports_guaranteed_tail_call(self) -> bool {323        match self {324            Self::CmseNonSecureCall | Self::CmseNonSecureEntry => {325                // See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers326                // before returning, and hence cannot guarantee a tail call.327                false328            }329            Self::AvrInterrupt330            | Self::AvrNonBlockingInterrupt331            | Self::Msp430Interrupt332            | Self::RiscvInterruptM333            | Self::RiscvInterruptS334            | Self::X86Interrupt => {335                // See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly.336                false337            }338            Self::GpuKernel | Self::PtxKernel => {339                // See https://godbolt.org/z/jq5TE5jK1.340                false341            }342            Self::Custom => {343                // This ABI does not support calls at all (except via assembly).344                false345            }346            Self::C { .. }347            | Self::System { .. }348            | Self::Rust349            | Self::RustCall350            | Self::RustCold351            | Self::RustInvalid352            | Self::Unadjusted353            | Self::EfiApi354            | Self::Aapcs { .. }355            | Self::Cdecl { .. }356            | Self::Stdcall { .. }357            | Self::Fastcall { .. }358            | Self::Thiscall { .. }359            | Self::Vectorcall { .. }360            | Self::SysV64 { .. }361            | Self::Win64 { .. }362            | Self::RustPreserveNone363            | Self::RustTail364            | Self::Swift => true,365        }366    }367}368369pub fn all_names() -> Vec<&'static str> {370    ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect()371}372373impl ExternAbi {374    /// Default ABI chosen for `extern fn` declarations without an explicit ABI.375    pub const FALLBACK: ExternAbi = ExternAbi::C { unwind: false };376377    pub fn name(self) -> &'static str {378        self.as_str()379    }380}381382impl fmt::Display for ExternAbi {383    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {384        write!(f, "\"{}\"", self.as_str())385    }386}

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.