|
| 1 | +use std::fmt; |
| 2 | + |
| 3 | +#[cfg(feature = "nightly")] |
| 4 | +use rustc_macros::HashStable_Generic; |
| 5 | + |
| 6 | +use crate::{AbiFromStrErr, ExternAbi}; |
| 7 | + |
| 8 | +/// Calling convention to determine codegen |
| 9 | +/// |
| 10 | +/// CanonAbi erases certain distinctions ExternAbi preserves, but remains target-dependent. |
| 11 | +/// There are still both target-specific variants and aliasing variants, though much fewer. |
| 12 | +/// The reason for this step is the frontend may wish to show an ExternAbi but implement that ABI |
| 13 | +/// using a different ABI than the string per se, or describe irrelevant differences, e.g. |
| 14 | +/// - extern "system" |
| 15 | +/// - extern "cdecl" |
| 16 | +/// - extern "C-unwind" |
| 17 | +/// In that sense, this erases mere syntactic distinctions to create a canonical *directive*, |
| 18 | +/// rather than picking the "actual" ABI. |
| 19 | +#[derive(Copy, Clone, Debug)] |
| 20 | +#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)] |
| 21 | +#[cfg_attr(feature = "nightly", derive(HashStable_Generic))] |
| 22 | +pub enum CanonAbi { |
| 23 | + // NOTE: the use of nested variants for some ABIs is for many targets they don't matter, |
| 24 | + // and this pushes the complexity of their reasoning to target-specific code, |
| 25 | + // allowing a `match` to easily exhaustively ignore these subcategories of variants. |
| 26 | + // Otherwise it is very tempting to avoid matching exhaustively! |
| 27 | + C, |
| 28 | + Rust, |
| 29 | + RustCold, |
| 30 | + |
| 31 | + /// ABIs relevant to 32-bit Arm targets |
| 32 | + Arm(ArmCall), |
| 33 | + /// ABI relevant to GPUs: the entry point for a GPU kernel |
| 34 | + GpuKernel, |
| 35 | + |
| 36 | + /// ABIs relevant to bare-metal interrupt targets |
| 37 | + // FIXME(workingjubilee): a particular reason for this nesting is we might not need these? |
| 38 | + // interrupt ABIs should have the same properties: |
| 39 | + // - uncallable by Rust calls, as LLVM rejects it in most cases |
| 40 | + // - uses a preserve-all-registers *callee* convention |
| 41 | + // - should always return `-> !` (effectively... it can't use normal `ret`) |
| 42 | + // what differs between targets is |
| 43 | + // - allowed arguments: x86 differs slightly, having 2-3 arguments which are handled magically |
| 44 | + // - may need special prologues/epilogues for some interrupts, without affecting "call ABI" |
| 45 | + Interrupt(InterruptKind), |
| 46 | + |
| 47 | + /// ABIs relevant to Windows or x86 targets |
| 48 | + X86(X86Call), |
| 49 | +} |
| 50 | + |
| 51 | +impl fmt::Display for CanonAbi { |
| 52 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 53 | + self.to_erased_extern_abi().as_str().fmt(f) |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +impl CanonAbi { |
| 58 | + /// convert to the ExternAbi that *shares a string* with this CanonAbi |
| 59 | + /// |
| 60 | + /// A target-insensitive mapping of CanonAbi to ExternAbi, convenient for "forwarding" impls. |
| 61 | + /// Importantly, the set of CanonAbi values is a logical *subset* of ExternAbi values, |
| 62 | + /// so this is injective: if you take an ExternAbi to a CanonAbi and back, you have lost data. |
| 63 | + const fn to_erased_extern_abi(self) -> ExternAbi { |
| 64 | + match self { |
| 65 | + CanonAbi::C => ExternAbi::C { unwind: false }, |
| 66 | + CanonAbi::Rust => ExternAbi::Rust, |
| 67 | + CanonAbi::RustCold => ExternAbi::RustCold, |
| 68 | + CanonAbi::Arm(arm_call) => match arm_call { |
| 69 | + ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false }, |
| 70 | + ArmCall::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall, |
| 71 | + ArmCall::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry, |
| 72 | + }, |
| 73 | + CanonAbi::GpuKernel => ExternAbi::GpuKernel, |
| 74 | + CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind { |
| 75 | + InterruptKind::Avr => ExternAbi::AvrInterrupt, |
| 76 | + InterruptKind::AvrNonBlocking => ExternAbi::AvrNonBlockingInterrupt, |
| 77 | + InterruptKind::Msp430 => ExternAbi::Msp430Interrupt, |
| 78 | + InterruptKind::RiscvMachine => ExternAbi::RiscvInterruptM, |
| 79 | + InterruptKind::RiscvSupervisor => ExternAbi::RiscvInterruptS, |
| 80 | + InterruptKind::X86 => ExternAbi::X86Interrupt, |
| 81 | + }, |
| 82 | + CanonAbi::X86(x86_call) => match x86_call { |
| 83 | + X86Call::Fastcall => ExternAbi::Fastcall { unwind: false }, |
| 84 | + X86Call::Stdcall => ExternAbi::Stdcall { unwind: false }, |
| 85 | + X86Call::SysV64 => ExternAbi::SysV64 { unwind: false }, |
| 86 | + X86Call::Thiscall => ExternAbi::Thiscall { unwind: false }, |
| 87 | + X86Call::Vectorcall => ExternAbi::Vectorcall { unwind: false }, |
| 88 | + X86Call::Win64 => ExternAbi::Win64 { unwind: false }, |
| 89 | + }, |
| 90 | + } |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +/// Callee codegen for interrupts |
| 95 | +/// |
| 96 | +/// This is named differently from the "Call" enums because it is different: |
| 97 | +/// these "ABI" differences are not relevant to callers, since there is "no caller". |
| 98 | +/// These only affect callee codegen. making their categorization as distinct ABIs a bit peculiar. |
| 99 | +#[derive(Copy, Clone, Debug)] |
| 100 | +#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)] |
| 101 | +#[cfg_attr(feature = "nightly", derive(HashStable_Generic))] |
| 102 | +pub enum InterruptKind { |
| 103 | + Avr, |
| 104 | + AvrNonBlocking, |
| 105 | + Msp430, |
| 106 | + RiscvMachine, |
| 107 | + RiscvSupervisor, |
| 108 | + X86, |
| 109 | +} |
| 110 | + |
| 111 | +/// ABIs defined for x86-{32,64} |
| 112 | +/// |
| 113 | +/// One of SysV64 or Win64 may alias the C ABI, and arguably Win64 is cross-platform now? |
| 114 | +#[derive(Clone, Copy, Debug)] |
| 115 | +#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)] |
| 116 | +#[cfg_attr(feature = "nightly", derive(HashStable_Generic))] |
| 117 | +pub enum X86Call { |
| 118 | + /// "fastcall" has both GNU and Windows variants |
| 119 | + Fastcall, |
| 120 | + /// "stdcall" has both GNU and Windows variants |
| 121 | + Stdcall, |
| 122 | + SysV64, |
| 123 | + Thiscall, |
| 124 | + Vectorcall, |
| 125 | + Win64, |
| 126 | +} |
| 127 | + |
| 128 | +/// ABIs defined for 32-bit Arm |
| 129 | +#[derive(Copy, Clone, Debug)] |
| 130 | +#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)] |
| 131 | +#[cfg_attr(feature = "nightly", derive(HashStable_Generic))] |
| 132 | +pub enum ArmCall { |
| 133 | + Aapcs, |
| 134 | + CCmseNonSecureCall, |
| 135 | + CCmseNonSecureEntry, |
| 136 | +} |
0 commit comments