Skip to content

Commit 50bcf75

Browse files
committed
Auto merge of #141569 - workingjubilee:canonicalize-abi, r=<try>
Replace ad-hoc ABI "adjustments" with an `AbiMap` to `CanonAbi` I am having second thoughts about some of my design choices, here, but I think it's useless to _internally_ debate them further. r? `@ghost` try-job: test-various try-job: aarch64-apple try-job: aarch64-gnu try-job: armhf-gnu try-job: x86_64-msvc-1 try-job: x86_64-msvc-2 try-job: x86_64-mingw-1 try-job: x86_64-mingw-2 try-job: i686-msvc-1 try-job: i686-msvc-2
2 parents 46264e6 + 7e6762a commit 50bcf75

File tree

221 files changed

+1558
-680
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

221 files changed

+1558
-680
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3251,6 +3251,8 @@ dependencies = [
32513251
"rustc_macros",
32523252
"rustc_serialize",
32533253
"rustc_span",
3254+
"serde",
3255+
"serde_json",
32543256
"tracing",
32553257
]
32563258

compiler/rustc_abi/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ rustc_index = { path = "../rustc_index", default-features = false }
1414
rustc_macros = { path = "../rustc_macros", optional = true }
1515
rustc_serialize = { path = "../rustc_serialize", optional = true }
1616
rustc_span = { path = "../rustc_span", optional = true }
17+
serde = { version = "1.0.200", features = ["derive"] }
18+
serde_json = "1.0.100"
1719
tracing = "0.1"
1820
# tidy-alphabetical-end
1921

compiler/rustc_abi/src/canon_abi.rs

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
2+
use std::fmt;
3+
use std::hash::{Hash, Hasher};
4+
use std::str::FromStr;
5+
6+
#[cfg(feature = "nightly")]
7+
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd};
8+
9+
use crate::{AbiFromStrErr, ExternAbi};
10+
11+
/// Calling convention to determine codegen
12+
///
13+
/// CanonAbi erases certain distinctions ExternAbi preserves, but remains target-dependent.
14+
/// There are still both target-specific variants and aliasing variants, though much fewer.
15+
/// The reason for this step is the frontend may wish to show an ExternAbi but implement that ABI
16+
/// using a different ABI than the string per se, or describe irrelevant differences, e.g.
17+
/// - extern "system"
18+
/// - extern "cdecl"
19+
/// - extern "C-unwind"
20+
/// In that sense, this erases mere syntactic distinctions to create a canonical *directive*,
21+
/// rather than picking the "actual" ABI.
22+
#[derive(Copy, Clone, Debug)]
23+
pub enum CanonAbi {
24+
// NOTE: the use of nested variants for some ABIs is for many targets they don't matter,
25+
// and this pushes the complexity of their reasoning to target-specific code,
26+
// allowing a `match` to easily exhaustively ignore these subcategories of variants.
27+
// Otherwise it is very tempting to avoid matching exhaustively!
28+
C,
29+
Rust,
30+
RustCold,
31+
32+
/// ABIs relevant to 32-bit Arm targets
33+
Arm(ArmCall),
34+
/// ABI relevant to GPUs: the entry point for a GPU kernel
35+
GpuKernel,
36+
37+
/// ABIs relevant to bare-metal interrupt targets
38+
// FIXME(workingjubilee): a particular reason for this nesting is we might not need these?
39+
// interrupt ABIs should have the same properties:
40+
// - uncallable by Rust calls, as LLVM rejects it in most cases
41+
// - uses a preserve-all-registers *callee* convention
42+
// - should always return `-> !` (effectively... it can't use normal `ret`)
43+
// what differs between targets is
44+
// - allowed arguments: x86 differs slightly, having 2-3 arguments which are handled magically
45+
// - may need special prologues/epilogues for some interrupts, without affecting "call ABI"
46+
Interrupt(InterruptKind),
47+
48+
/// ABIs relevant to Windows or x86 targets
49+
X86(X86Call),
50+
}
51+
52+
// For most "utility" impls, just forward CanonAbi to ExternAbi as it is notionally a subset
53+
impl Ord for CanonAbi {
54+
fn cmp(&self, other: &Self) -> Ordering {
55+
self.to_erased_extern_abi().cmp(&other.to_erased_extern_abi())
56+
}
57+
}
58+
59+
impl PartialOrd for CanonAbi {
60+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
61+
Some(self.cmp(&other))
62+
}
63+
}
64+
65+
impl PartialEq for CanonAbi {
66+
fn eq(&self, other: &Self) -> bool {
67+
self.cmp(&other) == Ordering::Equal
68+
}
69+
}
70+
71+
impl Eq for CanonAbi {}
72+
73+
impl Hash for CanonAbi {
74+
fn hash<H: Hasher>(&self, state: &mut H) {
75+
self.to_erased_extern_abi().hash(state)
76+
}
77+
}
78+
79+
// because we forward hashing to ExternAbi, also forward this:
80+
#[cfg(feature = "nightly")]
81+
impl<C> HashStable<C> for CanonAbi {
82+
#[inline]
83+
fn hash_stable(&self, _: &mut C, hasher: &mut StableHasher) {
84+
Hash::hash(self, hasher);
85+
}
86+
}
87+
88+
#[cfg(feature = "nightly")]
89+
impl StableOrd for CanonAbi {
90+
const CAN_USE_UNSTABLE_SORT: bool = true;
91+
92+
// because each ABI is hashed like a string, there is no possible instability
93+
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
94+
}
95+
96+
impl fmt::Display for CanonAbi {
97+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98+
self.to_erased_extern_abi().fmt(f)
99+
}
100+
}
101+
102+
impl serde::Serialize for CanonAbi {
103+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
104+
where
105+
S: serde::Serializer,
106+
{
107+
self.to_erased_extern_abi().as_str().serialize(serializer)
108+
}
109+
}
110+
111+
impl CanonAbi {
112+
/// convert to the ExternAbi that *shares a string* with this CanonAbi
113+
///
114+
/// NOT correct to use if you want to map CanonAbi to an ABI it may have been lowered from,
115+
/// but it is convenient for various "forwarding" implementations. Avoid exposing publicly!
116+
const fn to_erased_extern_abi(self) -> ExternAbi {
117+
match self {
118+
CanonAbi::C => ExternAbi::C { unwind: false },
119+
CanonAbi::Rust => ExternAbi::Rust,
120+
CanonAbi::RustCold => ExternAbi::RustCold,
121+
CanonAbi::Arm(arm_call) => match arm_call {
122+
ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },
123+
ArmCall::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall,
124+
ArmCall::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry,
125+
},
126+
CanonAbi::GpuKernel => ExternAbi::GpuKernel,
127+
CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
128+
InterruptKind::Avr => ExternAbi::AvrInterrupt,
129+
InterruptKind::AvrNonBlocking => ExternAbi::AvrNonBlockingInterrupt,
130+
InterruptKind::Msp430 => ExternAbi::Msp430Interrupt,
131+
InterruptKind::RiscvMachine => ExternAbi::RiscvInterruptM,
132+
InterruptKind::RiscvSupervisor => ExternAbi::RiscvInterruptS,
133+
InterruptKind::X86 => ExternAbi::X86Interrupt,
134+
},
135+
CanonAbi::X86(x86_call) => match x86_call {
136+
X86Call::Fastcall => ExternAbi::Fastcall { unwind: false },
137+
X86Call::Stdcall => ExternAbi::Stdcall { unwind: false },
138+
X86Call::SysV64 => ExternAbi::SysV64 { unwind: false },
139+
X86Call::Thiscall => ExternAbi::Thiscall { unwind: false },
140+
X86Call::Vectorcall => ExternAbi::Vectorcall { unwind: false },
141+
X86Call::Win64 => ExternAbi::Win64 { unwind: false },
142+
},
143+
}
144+
}
145+
146+
/// convert an ExternAbi to a CanonAbi if it maps directly
147+
///
148+
/// Pretty incorrect to publicly expose!
149+
const fn try_from_extern_abi(extern_abi: ExternAbi) -> Result<Self, AbiFromStrErr> {
150+
match extern_abi {
151+
ExternAbi::C { unwind: false } => Ok(Self::C),
152+
ExternAbi::Rust => Ok(Self::Rust),
153+
ExternAbi::RustCold => Ok(Self::RustCold),
154+
155+
// arm
156+
ExternAbi::Aapcs { unwind: false } => Ok(Self::Arm(ArmCall::Aapcs)),
157+
ExternAbi::CCmseNonSecureCall => Ok(Self::Arm(ArmCall::CCmseNonSecureCall)),
158+
ExternAbi::CCmseNonSecureEntry => Ok(Self::Arm(ArmCall::CCmseNonSecureEntry)),
159+
// gpu
160+
ExternAbi::GpuKernel => Ok(Self::GpuKernel),
161+
// interrupt
162+
ExternAbi::AvrInterrupt => Ok(Self::Interrupt(InterruptKind::Avr)),
163+
ExternAbi::AvrNonBlockingInterrupt => {
164+
Ok(Self::Interrupt(InterruptKind::AvrNonBlocking))
165+
}
166+
ExternAbi::Msp430Interrupt => Ok(Self::Interrupt(InterruptKind::Msp430)),
167+
ExternAbi::RiscvInterruptM => Ok(Self::Interrupt(InterruptKind::RiscvMachine)),
168+
ExternAbi::RiscvInterruptS => Ok(Self::Interrupt(InterruptKind::RiscvSupervisor)),
169+
ExternAbi::X86Interrupt => Ok(Self::Interrupt(InterruptKind::X86)),
170+
171+
// x86
172+
ExternAbi::Stdcall { unwind: false } => Ok(Self::X86(X86Call::Stdcall)),
173+
ExternAbi::Fastcall { unwind: false } => Ok(Self::X86(X86Call::Fastcall)),
174+
ExternAbi::Vectorcall { unwind: false } => Ok(Self::X86(X86Call::Vectorcall)),
175+
ExternAbi::Thiscall { unwind: false } => Ok(Self::X86(X86Call::Thiscall)),
176+
ExternAbi::Win64 { unwind: false } => Ok(Self::X86(X86Call::Win64)),
177+
ExternAbi::SysV64 { unwind: false } => Ok(Self::X86(X86Call::SysV64)),
178+
179+
// fanon
180+
ExternAbi::Cdecl { unwind: _ }
181+
| ExternAbi::EfiApi
182+
| ExternAbi::PtxKernel
183+
| ExternAbi::RustCall
184+
| ExternAbi::System { unwind: _ }
185+
| ExternAbi::Unadjusted => Err(AbiFromStrErr::Unknown),
186+
// unwind where it should not unwind
187+
ExternAbi::Aapcs { unwind: true }
188+
| ExternAbi::C { unwind: true }
189+
| ExternAbi::Stdcall { unwind: true }
190+
| ExternAbi::Fastcall { unwind: true }
191+
| ExternAbi::Vectorcall { unwind: true }
192+
| ExternAbi::Thiscall { unwind: true }
193+
| ExternAbi::Win64 { unwind: true }
194+
| ExternAbi::SysV64 { unwind: true } => Err(AbiFromStrErr::NoUnwind),
195+
}
196+
}
197+
}
198+
199+
impl TryFrom<ExternAbi> for CanonAbi {
200+
type Error = AbiFromStrErr;
201+
202+
fn try_from(extern_abi: ExternAbi) -> Result<Self, Self::Error> {
203+
CanonAbi::try_from_extern_abi(extern_abi)
204+
}
205+
}
206+
207+
impl FromStr for CanonAbi {
208+
type Err = AbiFromStrErr;
209+
210+
fn from_str(s: &str) -> Result<Self, Self::Err> {
211+
// parse as ExternAbi and propagate out errors, as CanonAbi uses the same errors
212+
let extern_abi = s.parse()?;
213+
CanonAbi::try_from_extern_abi(extern_abi)
214+
}
215+
}
216+
217+
/// Callee codegen for interrupts
218+
///
219+
/// This is named differently from the "Call" enums because it is different:
220+
/// these "ABI" differences are not relevant to callers, since there is "no caller".
221+
/// These only affect callee codegen. making their categorization as distinct ABIs a bit peculiar.
222+
#[derive(Copy, Clone, Debug)]
223+
pub enum InterruptKind {
224+
Avr,
225+
AvrNonBlocking,
226+
Msp430,
227+
RiscvMachine,
228+
RiscvSupervisor,
229+
X86,
230+
}
231+
232+
/// ABIs defined for x86-{32,64}
233+
///
234+
/// One of SysV64 or Win64 may alias the C ABI, and arguably Win64 is cross-platform now?
235+
#[derive(Clone, Copy, Debug)]
236+
pub enum X86Call {
237+
/// "fastcall" has both GNU and Windows variants
238+
Fastcall,
239+
/// "stdcall" has both GNU and Windows variants
240+
Stdcall,
241+
SysV64,
242+
Thiscall,
243+
Vectorcall,
244+
Win64,
245+
}
246+
247+
/// ABIs defined for 32-bit Arm
248+
#[derive(Copy, Clone, Debug)]
249+
pub enum ArmCall {
250+
Aapcs,
251+
CCmseNonSecureCall,
252+
CCmseNonSecureEntry,
253+
}

compiler/rustc_abi/src/extern_abi.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd};
77
#[cfg(feature = "nightly")]
88
use rustc_macros::{Decodable, Encodable};
99

10+
use crate::AbiFromStrErr;
11+
1012
#[cfg(test)]
1113
mod tests;
1214

@@ -99,11 +101,6 @@ macro_rules! abi_impls {
99101
}
100102
}
101103

102-
#[derive(Debug)]
103-
pub enum AbiFromStrErr {
104-
Unknown,
105-
}
106-
107104
abi_impls! {
108105
ExternAbi = {
109106
C { unwind: false } =><= "C",

compiler/rustc_abi/src/lib.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,20 @@ use rustc_index::{Idx, IndexSlice, IndexVec};
5555
use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_Generic};
5656

5757
mod callconv;
58+
mod canon_abi;
59+
mod extern_abi;
5860
mod layout;
61+
mod map;
5962
#[cfg(test)]
6063
mod tests;
6164

62-
mod extern_abi;
63-
6465
pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
66+
pub use canon_abi::{ArmCall, CanonAbi, InterruptKind, X86Call};
6567
pub use extern_abi::{ExternAbi, all_names};
6668
#[cfg(feature = "nightly")]
6769
pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
6870
pub use layout::{LayoutCalculator, LayoutCalculatorError};
71+
pub use map::{AbiMap, AbiNotNormal};
6972

7073
/// Requirements for a `StableHashingContext` to be used in this crate.
7174
/// This is a hack to allow using the `HashStable_Generic` derive macro
@@ -1895,3 +1898,28 @@ pub enum StructKind {
18951898
/// A univariant, but with a prefix of an arbitrary size & alignment (e.g., enum tag).
18961899
Prefixed(Size, Align),
18971900
}
1901+
1902+
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
1903+
pub enum AbiFromStrErr {
1904+
/// not a known ABI
1905+
Unknown,
1906+
/// no "-unwind" variant
1907+
NoUnwind,
1908+
}
1909+
1910+
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
1911+
pub enum AbiFromJsonErr {
1912+
InvalidType,
1913+
UnusedKey,
1914+
Parse { kind: AbiFromStrErr, value: String },
1915+
}
1916+
1917+
impl fmt::Display for AbiFromStrErr {
1918+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1919+
match self {
1920+
AbiFromStrErr::Unknown => "no such CanonAbi",
1921+
AbiFromStrErr::NoUnwind => "unwind is not a CanonAbi",
1922+
}
1923+
.fmt(f)
1924+
}
1925+
}

0 commit comments

Comments
 (0)