use std::fmt; #[cfg(feature = "nightly")] use rustc_macros::HashStable_Generic; use crate::ExternAbi; /// Calling convention to determine codegen /// /// CanonAbi erases certain distinctions ExternAbi preserves, but remains target-dependent. /// There are still both target-specific variants and aliasing variants, though much fewer. /// The reason for this step is the frontend may wish to show an ExternAbi but implement that ABI /// using a different ABI than the string per se, or describe irrelevant differences, e.g. /// - extern "system" /// - extern "cdecl" /// - extern "C-unwind" /// In that sense, this erases mere syntactic distinctions to create a canonical *directive*, /// rather than picking the "actual" ABI. #[derive(Copy, Clone, Debug)] #[derive(PartialOrd, Ord, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(HashStable_Generic))] pub enum CanonAbi { // NOTE: the use of nested variants for some ABIs is for many targets they don't matter, // and this pushes the complexity of their reasoning to target-specific code, // allowing a `match` to easily exhaustively ignore these subcategories of variants. // Otherwise it is very tempting to avoid matching exhaustively! C, Rust, RustCold, /// An ABI that rustc does not know how to call or define. Custom, /// ABIs relevant to 32-bit Arm targets Arm(ArmCall), /// ABI relevant to GPUs: the entry point for a GPU kernel GpuKernel, /// ABIs relevant to bare-metal interrupt targets // FIXME(workingjubilee): a particular reason for this nesting is we might not need these? // interrupt ABIs should have the same properties: // - uncallable by Rust calls, as LLVM rejects it in most cases // - uses a preserve-all-registers *callee* convention // - should always return `-> !` (effectively... it can't use normal `ret`) // what differs between targets is // - allowed arguments: x86 differs slightly, having 2-3 arguments which are handled magically // - may need special prologues/epilogues for some interrupts, without affecting "call ABI" Interrupt(InterruptKind), /// ABIs relevant to Windows or x86 targets X86(X86Call), } impl fmt::Display for CanonAbi { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // convert to the ExternAbi that *shares a string* with this CanonAbi. // FIXME: ideally we'd avoid printing `CanonAbi`, and preserve `ExternAbi` everywhere // that we need to generate error messages. let erased_abi = match self { CanonAbi::C => ExternAbi::C { unwind: false }, CanonAbi::Rust => ExternAbi::Rust, CanonAbi::RustCold => ExternAbi::RustCold, CanonAbi::Custom => ExternAbi::Custom, CanonAbi::Arm(arm_call) => match arm_call { ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false }, ArmCall::CCmseNonSecureCall => ExternAbi::CmseNonSecureCall, ArmCall::CCmseNonSecureEntry => ExternAbi::CmseNonSecureEntry, }, CanonAbi::GpuKernel => ExternAbi::GpuKernel, CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind { InterruptKind::Avr => ExternAbi::AvrInterrupt, InterruptKind::AvrNonBlocking => ExternAbi::AvrNonBlockingInterrupt, InterruptKind::Msp430 => ExternAbi::Msp430Interrupt, InterruptKind::RiscvMachine => ExternAbi::RiscvInterruptM, InterruptKind::RiscvSupervisor => ExternAbi::RiscvInterruptS, InterruptKind::X86 => ExternAbi::X86Interrupt, }, CanonAbi::X86(x86_call) => match x86_call { X86Call::Fastcall => ExternAbi::Fastcall { unwind: false }, X86Call::Stdcall => ExternAbi::Stdcall { unwind: false }, X86Call::SysV64 => ExternAbi::SysV64 { unwind: false }, X86Call::Thiscall => ExternAbi::Thiscall { unwind: false }, X86Call::Vectorcall => ExternAbi::Vectorcall { unwind: false }, X86Call::Win64 => ExternAbi::Win64 { unwind: false }, }, }; erased_abi.as_str().fmt(f) } } /// Callee codegen for interrupts /// /// This is named differently from the "Call" enums because it is different: /// these "ABI" differences are not relevant to callers, since there is "no caller". /// These only affect callee codegen. making their categorization as distinct ABIs a bit peculiar. #[derive(Copy, Clone, Debug)] #[derive(PartialOrd, Ord, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(HashStable_Generic))] pub enum InterruptKind { Avr, AvrNonBlocking, Msp430, RiscvMachine, RiscvSupervisor, X86, } /// ABIs defined for x86-{32,64} /// /// One of SysV64 or Win64 may alias the C ABI, and arguably Win64 is cross-platform now? #[derive(Clone, Copy, Debug)] #[derive(PartialOrd, Ord, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(HashStable_Generic))] pub enum X86Call { /// "fastcall" has both GNU and Windows variants Fastcall, /// "stdcall" has both GNU and Windows variants Stdcall, SysV64, Thiscall, Vectorcall, Win64, } /// ABIs defined for 32-bit Arm #[derive(Copy, Clone, Debug)] #[derive(PartialOrd, Ord, PartialEq, Eq, Hash)] #[cfg_attr(feature = "nightly", derive(HashStable_Generic))] pub enum ArmCall { Aapcs, CCmseNonSecureCall, CCmseNonSecureEntry, }