| use rustc_abi::{ArmCall, CanonAbi, ExternAbi, InterruptKind, X86Call}; | |
| use crate::spec::{Arch, Target}; | |
| /// Mapping for ExternAbi to CanonAbi according to a Target | |
| /// | |
| /// A maybe-transitional structure circa 2025 for hosting future experiments in | |
| /// encapsulating arch-specific ABI lowering details to make them more testable. | |
| #[derive(Clone, Debug)] | |
| pub struct AbiMap { | |
| arch: ArchKind, | |
| os: OsKind, | |
| } | |
| /// result from trying to map an ABI | |
| #[derive(Copy, Clone, Debug)] | |
| pub enum AbiMapping { | |
| /// this ABI is exactly mapped for this platform | |
| Direct(CanonAbi), | |
| /// we don't yet warn on this, but we will | |
| Deprecated(CanonAbi), | |
| /// ABI we do not map for this platform: it must not reach codegen | |
| Invalid, | |
| } | |
| impl AbiMapping { | |
| /// optionally get a [CanonAbi], even if Deprecated | |
| pub fn into_option(self) -> Option<CanonAbi> { | |
| match self { | |
| Self::Direct(abi) | Self::Deprecated(abi) => Some(abi), | |
| Self::Invalid => None, | |
| } | |
| } | |
| /// get a [CanonAbi] even if Deprecated, panicking if Invalid | |
| #[track_caller] | |
| pub fn unwrap(self) -> CanonAbi { | |
| self.into_option().unwrap() | |
| } | |
| pub fn is_mapped(self) -> bool { | |
| self.into_option().is_some() | |
| } | |
| } | |
| impl AbiMap { | |
| /// create an AbiMap according to arbitrary fields on the [Target] | |
| pub fn from_target(target: &Target) -> Self { | |
| // the purpose of this little exercise is to force listing what affects these mappings | |
| let arch = match target.arch { | |
| Arch::AArch64 => ArchKind::Aarch64, | |
| Arch::AmdGpu => ArchKind::Amdgpu, | |
| Arch::Arm => ArchKind::Arm(if target.llvm_target.starts_with("thumbv8m") { | |
| ArmVer::ThumbV8M | |
| } else { | |
| ArmVer::Other | |
| }), | |
| Arch::Avr => ArchKind::Avr, | |
| Arch::LoongArch32 | Arch::LoongArch64 => ArchKind::LoongArch, | |
| Arch::Msp430 => ArchKind::Msp430, | |
| Arch::Nvptx64 => ArchKind::Nvptx, | |
| Arch::RiscV32 | Arch::RiscV64 => ArchKind::Riscv, | |
| Arch::X86 => ArchKind::X86, | |
| Arch::X86_64 => ArchKind::X86_64, | |
| _ => ArchKind::Other, | |
| }; | |
| let os = if target.is_like_darwin { | |
| OsKind::Apple | |
| } else if target.is_like_windows { | |
| OsKind::Windows | |
| } else if target.is_like_vexos { | |
| OsKind::VEXos | |
| } else { | |
| OsKind::Other | |
| }; | |
| AbiMap { arch, os } | |
| } | |
| /// lower an [ExternAbi] to a [CanonAbi] if this AbiMap allows | |
| pub fn canonize_abi(&self, extern_abi: ExternAbi, has_c_varargs: bool) -> AbiMapping { | |
| let AbiMap { os, arch } = *self; | |
| if extern_abi == ExternAbi::Swift { | |
| // Per https://www.swift.org/blog/abi-stability-and-more/, Swift's ABI | |
| // is only stable on Apple platforms, so we reject it elsewhere. | |
| match os { | |
| OsKind::Apple => {} | |
| _ => return AbiMapping::Invalid, | |
| } | |
| } | |
| let canon_abi = match (extern_abi, arch) { | |
| // infallible lowerings | |
| (ExternAbi::C { .. }, _) => CanonAbi::C, | |
| (ExternAbi::Rust | ExternAbi::RustCall, _) => CanonAbi::Rust, | |
| (ExternAbi::Unadjusted, _) => CanonAbi::C, | |
| (ExternAbi::RustCold, _) if self.os == OsKind::Windows => CanonAbi::Rust, | |
| (ExternAbi::RustCold, _) => CanonAbi::RustCold, | |
| (ExternAbi::RustPreserveNone, _) => CanonAbi::RustPreserveNone, | |
| (ExternAbi::Custom, _) => CanonAbi::Custom, | |
| (ExternAbi::Swift, _) => CanonAbi::Swift, | |
| (ExternAbi::System { .. }, ArchKind::X86) | |
| if os == OsKind::Windows && !has_c_varargs => | |
| { | |
| CanonAbi::X86(X86Call::Stdcall) | |
| } | |
| (ExternAbi::System { .. }, ArchKind::Arm(..)) if self.os == OsKind::VEXos => { | |
| // Calls to VEXos APIs do not use VFP registers. | |
| CanonAbi::Arm(ArmCall::Aapcs) | |
| } | |
| (ExternAbi::System { .. }, _) => CanonAbi::C, | |
| // fallible lowerings | |
| /* multi-platform */ | |
| // always and forever | |
| (ExternAbi::RustInvalid, _) => return AbiMapping::Invalid, | |
| (ExternAbi::EfiApi, ArchKind::Arm(..)) => CanonAbi::Arm(ArmCall::Aapcs), | |
| (ExternAbi::EfiApi, ArchKind::X86_64) => CanonAbi::X86(X86Call::Win64), | |
| ( | |
| ExternAbi::EfiApi, | |
| ArchKind::Aarch64 | ArchKind::LoongArch | ArchKind::Riscv | ArchKind::X86, | |
| ) => CanonAbi::C, | |
| (ExternAbi::EfiApi, _) => return AbiMapping::Invalid, | |
| /* arm */ | |
| (ExternAbi::Aapcs { .. }, ArchKind::Arm(..)) => CanonAbi::Arm(ArmCall::Aapcs), | |
| (ExternAbi::Aapcs { .. }, _) => return AbiMapping::Invalid, | |
| (ExternAbi::CmseNonSecureCall, ArchKind::Arm(ArmVer::ThumbV8M)) => { | |
| CanonAbi::Arm(ArmCall::CCmseNonSecureCall) | |
| } | |
| (ExternAbi::CmseNonSecureEntry, ArchKind::Arm(ArmVer::ThumbV8M)) => { | |
| CanonAbi::Arm(ArmCall::CCmseNonSecureEntry) | |
| } | |
| (ExternAbi::CmseNonSecureCall | ExternAbi::CmseNonSecureEntry, ..) => { | |
| return AbiMapping::Invalid; | |
| } | |
| /* gpu */ | |
| (ExternAbi::PtxKernel, ArchKind::Nvptx) => CanonAbi::GpuKernel, | |
| (ExternAbi::GpuKernel, ArchKind::Amdgpu | ArchKind::Nvptx) => CanonAbi::GpuKernel, | |
| (ExternAbi::PtxKernel | ExternAbi::GpuKernel, _) => return AbiMapping::Invalid, | |
| /* x86 */ | |
| (ExternAbi::Cdecl { .. }, ArchKind::X86) => CanonAbi::C, | |
| (ExternAbi::Cdecl { .. }, _) => return AbiMapping::Deprecated(CanonAbi::C), | |
| (ExternAbi::Fastcall { .. }, ArchKind::X86) => CanonAbi::X86(X86Call::Fastcall), | |
| (ExternAbi::Fastcall { .. }, _) if os == OsKind::Windows => { | |
| return AbiMapping::Deprecated(CanonAbi::C); | |
| } | |
| (ExternAbi::Fastcall { .. }, _) => return AbiMapping::Invalid, | |
| (ExternAbi::Stdcall { .. }, ArchKind::X86) => CanonAbi::X86(X86Call::Stdcall), | |
| (ExternAbi::Stdcall { .. }, _) if os == OsKind::Windows => { | |
| return AbiMapping::Deprecated(CanonAbi::C); | |
| } | |
| (ExternAbi::Stdcall { .. }, _) => return AbiMapping::Invalid, | |
| (ExternAbi::Thiscall { .. }, ArchKind::X86) => CanonAbi::X86(X86Call::Thiscall), | |
| (ExternAbi::Thiscall { .. }, _) => return AbiMapping::Invalid, | |
| (ExternAbi::Vectorcall { .. }, ArchKind::X86 | ArchKind::X86_64) => { | |
| CanonAbi::X86(X86Call::Vectorcall) | |
| } | |
| (ExternAbi::Vectorcall { .. }, _) => return AbiMapping::Invalid, | |
| (ExternAbi::SysV64 { .. }, ArchKind::X86_64) => CanonAbi::X86(X86Call::SysV64), | |
| (ExternAbi::Win64 { .. }, ArchKind::X86_64) => CanonAbi::X86(X86Call::Win64), | |
| (ExternAbi::SysV64 { .. } | ExternAbi::Win64 { .. }, _) => return AbiMapping::Invalid, | |
| /* interrupts */ | |
| (ExternAbi::AvrInterrupt, ArchKind::Avr) => CanonAbi::Interrupt(InterruptKind::Avr), | |
| (ExternAbi::AvrNonBlockingInterrupt, ArchKind::Avr) => { | |
| CanonAbi::Interrupt(InterruptKind::AvrNonBlocking) | |
| } | |
| (ExternAbi::Msp430Interrupt, ArchKind::Msp430) => { | |
| CanonAbi::Interrupt(InterruptKind::Msp430) | |
| } | |
| (ExternAbi::RiscvInterruptM, ArchKind::Riscv) => { | |
| CanonAbi::Interrupt(InterruptKind::RiscvMachine) | |
| } | |
| (ExternAbi::RiscvInterruptS, ArchKind::Riscv) => { | |
| CanonAbi::Interrupt(InterruptKind::RiscvSupervisor) | |
| } | |
| (ExternAbi::X86Interrupt, ArchKind::X86 | ArchKind::X86_64) => { | |
| CanonAbi::Interrupt(InterruptKind::X86) | |
| } | |
| ( | |
| ExternAbi::AvrInterrupt | |
| | ExternAbi::AvrNonBlockingInterrupt | |
| | ExternAbi::Msp430Interrupt | |
| | ExternAbi::RiscvInterruptM | |
| | ExternAbi::RiscvInterruptS | |
| | ExternAbi::X86Interrupt, | |
| _, | |
| ) => return AbiMapping::Invalid, | |
| }; | |
| AbiMapping::Direct(canon_abi) | |
| } | |
| } | |
| #[derive(Debug, PartialEq, Copy, Clone)] | |
| enum ArchKind { | |
| Aarch64, | |
| Amdgpu, | |
| Arm(ArmVer), | |
| Avr, | |
| LoongArch, | |
| Msp430, | |
| Nvptx, | |
| Riscv, | |
| X86, | |
| X86_64, | |
| /// Architectures which don't need other considerations for ABI lowering | |
| Other, | |
| } | |
| #[derive(Debug, PartialEq, Copy, Clone)] | |
| enum OsKind { | |
| Apple, | |
| Windows, | |
| VEXos, | |
| Other, | |
| } | |
| #[derive(Debug, PartialEq, Copy, Clone)] | |
| enum ArmVer { | |
| ThumbV8M, | |
| Other, | |
| } |