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 /// An ABI that rustc does not know how to call or define.
32 Custom,
33
34 /// ABIs relevant to 32-bit Arm targets
35 Arm(ArmCall),
36 /// ABI relevant to GPUs: the entry point for a GPU kernel
37 GpuKernel,
38
39 /// ABIs relevant to bare-metal interrupt targets
40 // FIXME(workingjubilee): a particular reason for this nesting is we might not need these?
41 // interrupt ABIs should have the same properties:
42 // - uncallable by Rust calls, as LLVM rejects it in most cases
43 // - uses a preserve-all-registers *callee* convention
44 // - should always return `-> !` (effectively... it can't use normal `ret`)
45 // what differs between targets is
46 // - allowed arguments: x86 differs slightly, having 2-3 arguments which are handled magically
47 // - may need special prologues/epilogues for some interrupts, without affecting "call ABI"
48 Interrupt(InterruptKind),
49
50 /// ABIs relevant to Windows or x86 targets
51 X86(X86Call),
52}
53
54impl CanonAbi {
55 pub fn is_rustic_abi(self) -> bool {
56 match self {
57 CanonAbi::Rust | CanonAbi::RustCold => true,
58 CanonAbi::C
59 | CanonAbi::Custom
60 | CanonAbi::Arm(_)
61 | CanonAbi::GpuKernel
62 | CanonAbi::Interrupt(_)
63 | CanonAbi::X86(_) => false,
64 }
65 }
66}
67
68impl fmt::Display for CanonAbi {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 // convert to the ExternAbi that *shares a string* with this CanonAbi.
71 // FIXME: ideally we'd avoid printing `CanonAbi`, and preserve `ExternAbi` everywhere
72 // that we need to generate error messages.
73 let erased_abi = match self {
74 CanonAbi::C => ExternAbi::C { unwind: false },
75 CanonAbi::Rust => ExternAbi::Rust,
76 CanonAbi::RustCold => ExternAbi::RustCold,
77 CanonAbi::Custom => ExternAbi::Custom,
78 CanonAbi::Arm(arm_call) => match arm_call {
79 ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },
80 ArmCall::CCmseNonSecureCall => ExternAbi::CmseNonSecureCall,
81 ArmCall::CCmseNonSecureEntry => ExternAbi::CmseNonSecureEntry,
82 },
83 CanonAbi::GpuKernel => ExternAbi::GpuKernel,
84 CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
85 InterruptKind::Avr => ExternAbi::AvrInterrupt,
86 InterruptKind::AvrNonBlocking => ExternAbi::AvrNonBlockingInterrupt,
87 InterruptKind::Msp430 => ExternAbi::Msp430Interrupt,
88 InterruptKind::RiscvMachine => ExternAbi::RiscvInterruptM,
89 InterruptKind::RiscvSupervisor => ExternAbi::RiscvInterruptS,
90 InterruptKind::X86 => ExternAbi::X86Interrupt,
91 },
92 CanonAbi::X86(x86_call) => match x86_call {
93 X86Call::Fastcall => ExternAbi::Fastcall { unwind: false },
94 X86Call::Stdcall => ExternAbi::Stdcall { unwind: false },
95 X86Call::SysV64 => ExternAbi::SysV64 { unwind: false },
96 X86Call::Thiscall => ExternAbi::Thiscall { unwind: false },
97 X86Call::Vectorcall => ExternAbi::Vectorcall { unwind: false },
98 X86Call::Win64 => ExternAbi::Win64 { unwind: false },
99 },
100 };
101 erased_abi.as_str().fmt(f)
102 }
103}
104
105/// Callee codegen for interrupts
106///
107/// This is named differently from the "Call" enums because it is different:
108/// these "ABI" differences are not relevant to callers, since there is "no caller".
109/// These only affect callee codegen. making their categorization as distinct ABIs a bit peculiar.
110#[derive(Copy, Clone, Debug)]
111#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
112#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
113pub enum InterruptKind {
114 Avr,
115 AvrNonBlocking,
116 Msp430,
117 RiscvMachine,
118 RiscvSupervisor,
119 X86,
120}
121
122/// ABIs defined for x86-{32,64}
123///
124/// One of SysV64 or Win64 may alias the C ABI, and arguably Win64 is cross-platform now?
125#[derive(Clone, Copy, Debug)]
126#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
127#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
128pub enum X86Call {
129 /// "fastcall" has both GNU and Windows variants
130 Fastcall,
131 /// "stdcall" has both GNU and Windows variants
132 Stdcall,
133 SysV64,
134 Thiscall,
135 Vectorcall,
136 Win64,
137}
138
139/// ABIs defined for 32-bit Arm
140#[derive(Copy, Clone, Debug)]
141#[derive(PartialOrd, Ord, PartialEq, Eq, Hash)]
142#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
143pub enum ArmCall {
144 Aapcs,
145 CCmseNonSecureCall,
146 CCmseNonSecureEntry,
147}