Skip to content

Commit f1f3d3c

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 + 71abc30 commit f1f3d3c

File tree

221 files changed

+1588
-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

+1588
-680
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3251,6 +3251,7 @@ dependencies = [
32513251
"rustc_macros",
32523252
"rustc_serialize",
32533253
"rustc_span",
3254+
"serde_json",
32543255
"tracing",
32553256
]
32563257

compiler/rustc_abi/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ 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_json = "1"
1718
tracing = "0.1"
1819
# tidy-alphabetical-end
1920

compiler/rustc_abi/src/canon_abi.rs

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
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().as_str().fmt(f)
99+
}
100+
}
101+
102+
impl CanonAbi {
103+
/// convert to the ExternAbi that *shares a string* with this CanonAbi
104+
///
105+
/// NOT correct to use if you want to map CanonAbi to an ABI it may have been lowered from,
106+
/// but it is convenient for various "forwarding" implementations. Avoid exposing publicly!
107+
const fn to_erased_extern_abi(self) -> ExternAbi {
108+
match self {
109+
CanonAbi::C => ExternAbi::C { unwind: false },
110+
CanonAbi::Rust => ExternAbi::Rust,
111+
CanonAbi::RustCold => ExternAbi::RustCold,
112+
CanonAbi::Arm(arm_call) => match arm_call {
113+
ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },
114+
ArmCall::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall,
115+
ArmCall::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry,
116+
},
117+
CanonAbi::GpuKernel => ExternAbi::GpuKernel,
118+
CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
119+
InterruptKind::Avr => ExternAbi::AvrInterrupt,
120+
InterruptKind::AvrNonBlocking => ExternAbi::AvrNonBlockingInterrupt,
121+
InterruptKind::Msp430 => ExternAbi::Msp430Interrupt,
122+
InterruptKind::RiscvMachine => ExternAbi::RiscvInterruptM,
123+
InterruptKind::RiscvSupervisor => ExternAbi::RiscvInterruptS,
124+
InterruptKind::X86 => ExternAbi::X86Interrupt,
125+
},
126+
CanonAbi::X86(x86_call) => match x86_call {
127+
X86Call::Fastcall => ExternAbi::Fastcall { unwind: false },
128+
X86Call::Stdcall => ExternAbi::Stdcall { unwind: false },
129+
X86Call::SysV64 => ExternAbi::SysV64 { unwind: false },
130+
X86Call::Thiscall => ExternAbi::Thiscall { unwind: false },
131+
X86Call::Vectorcall => ExternAbi::Vectorcall { unwind: false },
132+
X86Call::Win64 => ExternAbi::Win64 { unwind: false },
133+
},
134+
}
135+
}
136+
137+
/// convert an ExternAbi to a CanonAbi if it maps directly
138+
///
139+
/// Pretty incorrect to publicly expose!
140+
const fn try_from_extern_abi(extern_abi: ExternAbi) -> Result<Self, AbiFromStrErr> {
141+
match extern_abi {
142+
ExternAbi::C { unwind: false } => Ok(Self::C),
143+
ExternAbi::Rust => Ok(Self::Rust),
144+
ExternAbi::RustCold => Ok(Self::RustCold),
145+
146+
// arm
147+
ExternAbi::Aapcs { unwind: false } => Ok(Self::Arm(ArmCall::Aapcs)),
148+
ExternAbi::CCmseNonSecureCall => Ok(Self::Arm(ArmCall::CCmseNonSecureCall)),
149+
ExternAbi::CCmseNonSecureEntry => Ok(Self::Arm(ArmCall::CCmseNonSecureEntry)),
150+
// gpu
151+
ExternAbi::GpuKernel => Ok(Self::GpuKernel),
152+
// interrupt
153+
ExternAbi::AvrInterrupt => Ok(Self::Interrupt(InterruptKind::Avr)),
154+
ExternAbi::AvrNonBlockingInterrupt => {
155+
Ok(Self::Interrupt(InterruptKind::AvrNonBlocking))
156+
}
157+
ExternAbi::Msp430Interrupt => Ok(Self::Interrupt(InterruptKind::Msp430)),
158+
ExternAbi::RiscvInterruptM => Ok(Self::Interrupt(InterruptKind::RiscvMachine)),
159+
ExternAbi::RiscvInterruptS => Ok(Self::Interrupt(InterruptKind::RiscvSupervisor)),
160+
ExternAbi::X86Interrupt => Ok(Self::Interrupt(InterruptKind::X86)),
161+
162+
// x86
163+
ExternAbi::Stdcall { unwind: false } => Ok(Self::X86(X86Call::Stdcall)),
164+
ExternAbi::Fastcall { unwind: false } => Ok(Self::X86(X86Call::Fastcall)),
165+
ExternAbi::Vectorcall { unwind: false } => Ok(Self::X86(X86Call::Vectorcall)),
166+
ExternAbi::Thiscall { unwind: false } => Ok(Self::X86(X86Call::Thiscall)),
167+
ExternAbi::Win64 { unwind: false } => Ok(Self::X86(X86Call::Win64)),
168+
ExternAbi::SysV64 { unwind: false } => Ok(Self::X86(X86Call::SysV64)),
169+
170+
// fanon
171+
ExternAbi::Cdecl { unwind: _ }
172+
| ExternAbi::EfiApi
173+
| ExternAbi::PtxKernel
174+
| ExternAbi::RustCall
175+
| ExternAbi::System { unwind: _ }
176+
| ExternAbi::Unadjusted => Err(AbiFromStrErr::Unknown),
177+
// unwind where it should not unwind
178+
ExternAbi::Aapcs { unwind: true }
179+
| ExternAbi::C { unwind: true }
180+
| ExternAbi::Stdcall { unwind: true }
181+
| ExternAbi::Fastcall { unwind: true }
182+
| ExternAbi::Vectorcall { unwind: true }
183+
| ExternAbi::Thiscall { unwind: true }
184+
| ExternAbi::Win64 { unwind: true }
185+
| ExternAbi::SysV64 { unwind: true } => Err(AbiFromStrErr::NoUnwind),
186+
}
187+
}
188+
}
189+
190+
impl FromStr for CanonAbi {
191+
type Err = AbiFromStrErr;
192+
193+
fn from_str(s: &str) -> Result<Self, Self::Err> {
194+
// parse as ExternAbi and propagate out errors, as CanonAbi uses the same errors
195+
let extern_abi = s.parse()?;
196+
CanonAbi::try_from_extern_abi(extern_abi)
197+
}
198+
}
199+
200+
/// Callee codegen for interrupts
201+
///
202+
/// This is named differently from the "Call" enums because it is different:
203+
/// these "ABI" differences are not relevant to callers, since there is "no caller".
204+
/// These only affect callee codegen. making their categorization as distinct ABIs a bit peculiar.
205+
#[derive(Copy, Clone, Debug)]
206+
pub enum InterruptKind {
207+
Avr,
208+
AvrNonBlocking,
209+
Msp430,
210+
RiscvMachine,
211+
RiscvSupervisor,
212+
X86,
213+
}
214+
215+
/// ABIs defined for x86-{32,64}
216+
///
217+
/// One of SysV64 or Win64 may alias the C ABI, and arguably Win64 is cross-platform now?
218+
#[derive(Clone, Copy, Debug)]
219+
pub enum X86Call {
220+
/// "fastcall" has both GNU and Windows variants
221+
Fastcall,
222+
/// "stdcall" has both GNU and Windows variants
223+
Stdcall,
224+
SysV64,
225+
Thiscall,
226+
Vectorcall,
227+
Win64,
228+
}
229+
230+
/// ABIs defined for 32-bit Arm
231+
#[derive(Copy, Clone, Debug)]
232+
pub enum ArmCall {
233+
Aapcs,
234+
CCmseNonSecureCall,
235+
CCmseNonSecureEntry,
236+
}

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: 20 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,18 @@ 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+
}

0 commit comments

Comments
 (0)