rustc_abi/
canon_abi.rs

1use std::fmt;
2
3#[cfg(feature = "nightly")]
4use rustc_macros::HashStable_Generic;
5
6use crate::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))]
22pub 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
51impl fmt::Display for CanonAbi {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        // convert to the ExternAbi that *shares a string* with this CanonAbi.
54        // FIXME: ideally we'd avoid printing `CanonAbi`, and preserve `ExternAbi` everywhere
55        // that we need to generate error messages.
56        let erased_abi = match self {
57            CanonAbi::C => ExternAbi::C { unwind: false },
58            CanonAbi::Rust => ExternAbi::Rust,
59            CanonAbi::RustCold => ExternAbi::RustCold,
60            CanonAbi::Arm(arm_call) => match arm_call {
61                ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },
62                ArmCall::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall,
63                ArmCall::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry,
64            },
65            CanonAbi::GpuKernel => ExternAbi::GpuKernel,
66            CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
67                InterruptKind::Avr => ExternAbi::AvrInterrupt,
68                InterruptKind::AvrNonBlocking => ExternAbi::AvrNonBlockingInterrupt,
69                InterruptKind::Msp430 => ExternAbi::Msp430Interrupt,
70                InterruptKind::RiscvMachine => ExternAbi::RiscvInterruptM,
71                InterruptKind::RiscvSupervisor => ExternAbi::RiscvInterruptS,
72                InterruptKind::X86 => ExternAbi::X86Interrupt,
73            },
74            CanonAbi::X86(x86_call) => match x86_call {
75                X86Call::Fastcall => ExternAbi::Fastcall { unwind: false },
76                X86Call::Stdcall => ExternAbi::Stdcall { unwind: false },
77                X86Call::SysV64 => ExternAbi::SysV64 { unwind: false },
78                X86Call::Thiscall => ExternAbi::Thiscall { unwind: false },
79                X86Call::Vectorcall => ExternAbi::Vectorcall { unwind: false },
80                X86Call::Win64 => ExternAbi::Win64 { unwind: false },
81            },
82        };
83        erased_abi.as_str().fmt(f)
84    }
85}
86
87/// Callee codegen for interrupts
88///
89/// This is named differently from the "Call" enums because it is different:
90/// these "ABI" differences are not relevant to callers, since there is "no caller".
91/// These only affect callee codegen. making their categorization as distinct ABIs a bit peculiar.
92#[derive(Copy, Clone, Debug)]
93#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
94#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
95pub enum InterruptKind {
96    Avr,
97    AvrNonBlocking,
98    Msp430,
99    RiscvMachine,
100    RiscvSupervisor,
101    X86,
102}
103
104/// ABIs defined for x86-{32,64}
105///
106/// One of SysV64 or Win64 may alias the C ABI, and arguably Win64 is cross-platform now?
107#[derive(Clone, Copy, Debug)]
108#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
109#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
110pub enum X86Call {
111    /// "fastcall" has both GNU and Windows variants
112    Fastcall,
113    /// "stdcall" has both GNU and Windows variants
114    Stdcall,
115    SysV64,
116    Thiscall,
117    Vectorcall,
118    Win64,
119}
120
121/// ABIs defined for 32-bit Arm
122#[derive(Copy, Clone, Debug)]
123#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
124#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
125pub enum ArmCall {
126    Aapcs,
127    CCmseNonSecureCall,
128    CCmseNonSecureEntry,
129}