Skip to content

Commit b508329

Browse files
Auto merge of #141569 - workingjubilee:canonicalize-abi, r=<try>
Replace ad-hoc ABI "adjustments" with an `AbiMap` to `CanonAbi` Our `conv_from_spec_abi`, `adjust_abi`, and `is_abi_supported` combine to give us a very confusing way of reasoning about what _actual_ calling convention we want to lower our code to and whether we want to compile the resulting code at all. Instead of leaving this code as a miniature adventure game in which someone tries to combine stateful mutations into a Rube Goldberg machine that will let them escape the maze and arrive at the promised land of codegen, we let `AbiMap` devour this complexity. Once you have an `AbiMap`, you can answer which `ExternAbi`s will lower to what `CanonAbi`s (and whether they will lower at all). Removed: - `conv_from_spec_abi` replaced by `AbiMap::canonize_abi` - `adjust_abi` replaced by same - `Conv::PreserveAll` as unused - `Conv::Cold` as unused - `enum Conv` replaced by `enum CanonAbi` target-spec.json changes: - If you have a target-spec.json then now your "entry-abi" key will be specified in terms of one of the `"{abi}"` strings Rust recognizes, e.g. ```json "entry-abi": "C", "entry-abi": "win64", "entry-abi": "aapcs", ``` try-job: test-various try-job: aarch64-apple try-job: aarch64-gnu-debug try-job: arm-android 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 try-job: i686-mingw-1 try-job: i686-mingw-2
2 parents 15825b7 + da98b14 commit b508329

File tree

47 files changed

+851
-781
lines changed

Some content is hidden

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

47 files changed

+851
-781
lines changed

compiler/rustc_abi/src/canon_abi.rs

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

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: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,14 @@ 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;
5961
#[cfg(test)]
6062
mod tests;
6163

62-
mod extern_abi;
63-
6464
pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
65+
pub use canon_abi::{ArmCall, CanonAbi, InterruptKind, X86Call};
6566
pub use extern_abi::{ExternAbi, all_names};
6667
#[cfg(feature = "nightly")]
6768
pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
@@ -1895,3 +1896,11 @@ pub enum StructKind {
18951896
/// A univariant, but with a prefix of an arbitrary size & alignment (e.g., enum tag).
18961897
Prefixed(Size, Align),
18971898
}
1899+
1900+
#[derive(Clone, Debug)]
1901+
pub enum AbiFromStrErr {
1902+
/// not a known ABI
1903+
Unknown,
1904+
/// no "-unwind" variant
1905+
NoUnwind,
1906+
}

compiler/rustc_codegen_cranelift/src/abi/mod.rs

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::mem;
1010
use cranelift_codegen::ir::{ArgumentPurpose, SigRef};
1111
use cranelift_codegen::isa::CallConv;
1212
use cranelift_module::ModuleError;
13-
use rustc_abi::ExternAbi;
13+
use rustc_abi::{CanonAbi, ExternAbi, X86Call};
1414
use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization;
1515
use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall;
1616
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@@ -19,7 +19,7 @@ use rustc_middle::ty::layout::FnAbiOf;
1919
use rustc_middle::ty::print::with_no_trimmed_paths;
2020
use rustc_session::Session;
2121
use rustc_span::source_map::Spanned;
22-
use rustc_target::callconv::{Conv, FnAbi, PassMode};
22+
use rustc_target::callconv::{FnAbi, PassMode};
2323
use smallvec::SmallVec;
2424

2525
use self::pass_mode::*;
@@ -42,32 +42,27 @@ fn clif_sig_from_fn_abi<'tcx>(
4242
Signature { params, returns, call_conv }
4343
}
4444

45-
pub(crate) fn conv_to_call_conv(sess: &Session, c: Conv, default_call_conv: CallConv) -> CallConv {
45+
pub(crate) fn conv_to_call_conv(
46+
sess: &Session,
47+
c: CanonAbi,
48+
default_call_conv: CallConv,
49+
) -> CallConv {
4650
match c {
47-
Conv::Rust | Conv::C => default_call_conv,
48-
Conv::Cold | Conv::PreserveMost | Conv::PreserveAll => CallConv::Cold,
49-
Conv::X86_64SysV => CallConv::SystemV,
50-
Conv::X86_64Win64 => CallConv::WindowsFastcall,
51-
52-
// Should already get a back compat warning
53-
Conv::X86Fastcall | Conv::X86Stdcall | Conv::X86ThisCall | Conv::X86VectorCall => {
54-
default_call_conv
55-
}
56-
57-
Conv::X86Intr | Conv::RiscvInterrupt { .. } => {
58-
sess.dcx().fatal(format!("interrupt call conv {c:?} not yet implemented"))
51+
CanonAbi::Rust | CanonAbi::C => default_call_conv,
52+
CanonAbi::RustCold => CallConv::Cold,
53+
54+
CanonAbi::X86(x86_call) => match x86_call {
55+
X86Call::SysV64 => CallConv::SystemV,
56+
X86Call::Win64 => CallConv::WindowsFastcall,
57+
// Should already get a back compat warning
58+
_ => default_call_conv,
59+
},
60+
61+
CanonAbi::Interrupt(_) | CanonAbi::Arm(_) => {
62+
sess.dcx().fatal("call conv {c:?} is not yet implemented")
5963
}
60-
61-
Conv::ArmAapcs => sess.dcx().fatal("aapcs call conv not yet implemented"),
62-
Conv::CCmseNonSecureCall => {
63-
sess.dcx().fatal("C-cmse-nonsecure-call call conv is not yet implemented");
64-
}
65-
Conv::CCmseNonSecureEntry => {
66-
sess.dcx().fatal("C-cmse-nonsecure-entry call conv is not yet implemented");
67-
}
68-
69-
Conv::Msp430Intr | Conv::GpuKernel | Conv::AvrInterrupt | Conv::AvrNonBlockingInterrupt => {
70-
unreachable!("tried to use {c:?} call conv which only exists on an unsupported target");
64+
CanonAbi::GpuKernel => {
65+
unreachable!("tried to use {c:?} call conv which only exists on an unsupported target")
7166
}
7267
}
7368
}
@@ -610,7 +605,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
610605
target: CallTarget,
611606
call_args: &mut Vec<Value>,
612607
) {
613-
if fn_abi.conv != Conv::C {
608+
if fn_abi.conv != CanonAbi::C {
614609
fx.tcx.dcx().span_fatal(
615610
source_info.span,
616611
format!("Variadic call for non-C abi {:?}", fn_abi.conv),

compiler/rustc_codegen_gcc/src/abi.rs

Lines changed: 28 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#[cfg(feature = "master")]
22
use gccjit::FnAttribute;
33
use gccjit::{ToLValue, ToRValue, Type};
4-
use rustc_abi::{Reg, RegKind};
4+
use rustc_abi::{ArmCall, CanonAbi, InterruptKind, Reg, RegKind, X86Call};
55
use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeCodegenMethods};
66
use rustc_data_structures::fx::FxHashSet;
77
use rustc_middle::bug;
@@ -10,8 +10,6 @@ use rustc_middle::ty::layout::LayoutOf;
1010
#[cfg(feature = "master")]
1111
use rustc_session::config;
1212
use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode};
13-
#[cfg(feature = "master")]
14-
use rustc_target::callconv::{Conv, RiscvInterruptKind};
1513

1614
use crate::builder::Builder;
1715
use crate::context::CodegenCx;
@@ -238,29 +236,16 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
238236
}
239237

240238
#[cfg(feature = "master")]
241-
pub fn conv_to_fn_attribute<'gcc>(conv: Conv, arch: &str) -> Option<FnAttribute<'gcc>> {
239+
pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &str) -> Option<FnAttribute<'gcc>> {
242240
let attribute = match conv {
243-
Conv::C | Conv::Rust => return None,
244-
Conv::CCmseNonSecureCall => {
245-
if arch == "arm" {
246-
FnAttribute::ArmCmseNonsecureCall
247-
} else {
248-
return None;
249-
}
250-
}
251-
Conv::CCmseNonSecureEntry => {
252-
if arch == "arm" {
253-
FnAttribute::ArmCmseNonsecureEntry
254-
} else {
255-
return None;
256-
}
257-
}
258-
Conv::Cold => FnAttribute::Cold,
259-
// NOTE: the preserve attributes are not yet implemented in GCC:
260-
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110899
261-
Conv::PreserveMost => return None,
262-
Conv::PreserveAll => return None,
263-
Conv::GpuKernel => {
241+
CanonAbi::C | CanonAbi::Rust => return None,
242+
CanonAbi::Arm(arm_call) => match arm_call {
243+
ArmCall::CCmseNonSecureCall => FnAttribute::ArmCmseNonsecureCall,
244+
ArmCall::CCmseNonSecureEntry => FnAttribute::ArmCmseNonsecureEntry,
245+
ArmCall::Aapcs => FnAttribute::ArmPcs("aapcs"),
246+
},
247+
CanonAbi::RustCold => FnAttribute::Cold,
248+
CanonAbi::GpuKernel => {
264249
if arch == "amdgpu" {
265250
FnAttribute::GcnAmdGpuHsaKernel
266251
} else if arch == "nvptx64" {
@@ -270,26 +255,24 @@ pub fn conv_to_fn_attribute<'gcc>(conv: Conv, arch: &str) -> Option<FnAttribute<
270255
}
271256
}
272257
// TODO(antoyo): check if those AVR attributes are mapped correctly.
273-
Conv::AvrInterrupt => FnAttribute::AvrSignal,
274-
Conv::AvrNonBlockingInterrupt => FnAttribute::AvrInterrupt,
275-
Conv::ArmAapcs => FnAttribute::ArmPcs("aapcs"),
276-
Conv::Msp430Intr => FnAttribute::Msp430Interrupt,
277-
Conv::RiscvInterrupt { kind } => {
278-
let kind = match kind {
279-
RiscvInterruptKind::Machine => "machine",
280-
RiscvInterruptKind::Supervisor => "supervisor",
281-
};
282-
FnAttribute::RiscvInterrupt(kind)
283-
}
284-
Conv::X86Fastcall => FnAttribute::X86FastCall,
285-
Conv::X86Intr => FnAttribute::X86Interrupt,
286-
Conv::X86Stdcall => FnAttribute::X86Stdcall,
287-
Conv::X86ThisCall => FnAttribute::X86ThisCall,
288-
// NOTE: the vectorcall calling convention is not yet implemented in GCC:
289-
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89485
290-
Conv::X86VectorCall => return None,
291-
Conv::X86_64SysV => FnAttribute::X86SysvAbi,
292-
Conv::X86_64Win64 => FnAttribute::X86MsAbi,
258+
CanonAbi::Interrupt(interrupt_kind) => match interrupt_kind {
259+
InterruptKind::Avr => FnAttribute::AvrSignal,
260+
InterruptKind::AvrNonBlocking => FnAttribute::AvrInterrupt,
261+
InterruptKind::Msp430 => FnAttribute::Msp430Interrupt,
262+
InterruptKind::RiscvMachine => FnAttribute::RiscvInterrupt("machine"),
263+
InterruptKind::RiscvSupervisor => FnAttribute::RiscvInterrupt("supervisor"),
264+
InterruptKind::X86 => FnAttribute::X86Interrupt,
265+
},
266+
CanonAbi::X86(x86_call) => match x86_call {
267+
X86Call::Fastcall => FnAttribute::X86FastCall,
268+
X86Call::Stdcall => FnAttribute::X86Stdcall,
269+
X86Call::Thiscall => FnAttribute::X86ThisCall,
270+
// // NOTE: the vectorcall calling convention is not yet implemented in GCC:
271+
// // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89485
272+
X86Call::Vectorcall => return None,
273+
X86Call::SysV64 => FnAttribute::X86SysvAbi,
274+
X86Call::Win64 => FnAttribute::X86MsAbi,
275+
},
293276
};
294277
Some(attribute)
295278
}

compiler/rustc_codegen_gcc/src/int.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
//! 128-bit integers on 32-bit platforms and thus require to be handled manually.
44
55
use gccjit::{BinaryOp, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp};
6-
use rustc_abi::{Endian, ExternAbi};
6+
use rustc_abi::{CanonAbi, Endian, ExternAbi};
77
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
88
use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, BuilderMethods, OverflowOp};
99
use rustc_middle::ty::{self, Ty};
10-
use rustc_target::callconv::{ArgAbi, ArgAttributes, Conv, FnAbi, PassMode};
10+
use rustc_target::callconv::{ArgAbi, ArgAttributes, FnAbi, PassMode};
1111

1212
use crate::builder::{Builder, ToGccComp};
1313
use crate::common::{SignType, TypeReflection};
@@ -397,7 +397,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
397397
ret: arg_abi,
398398
c_variadic: false,
399399
fixed_count: 3,
400-
conv: Conv::C,
400+
conv: CanonAbi::C,
401401
can_unwind: false,
402402
};
403403
fn_abi.adjust_for_foreign_abi(self.cx, ExternAbi::C { unwind: false });

0 commit comments

Comments
 (0)