1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
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,
}
|