about summary refs log tree commit diff
path: root/compiler/rustc_abi/src
diff options
context:
space:
mode:
authorMatthias Krüger <476013+matthiaskrgr@users.noreply.github.com>2025-06-03 21:53:36 +0200
committerGitHub <noreply@github.com>2025-06-03 21:53:36 +0200
commit644f06ec1fd8b17de40d42c699a61020db02a123 (patch)
tree25c8aa492416b29c06ffbf29977bc19622b50204 /compiler/rustc_abi/src
parentd096ebf8d93e78b429c5c5866ecc60b3a5d9ed18 (diff)
parent307a18dc537fa3f5ee406f0d21c60c26bea18ec8 (diff)
downloadrust-644f06ec1fd8b17de40d42c699a61020db02a123.tar.gz
rust-644f06ec1fd8b17de40d42c699a61020db02a123.zip
Rollup merge of #141569 - workingjubilee:canonicalize-abi, r=bjorn3
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",
```
Diffstat (limited to 'compiler/rustc_abi/src')
-rw-r--r--compiler/rustc_abi/src/canon_abi.rs136
-rw-r--r--compiler/rustc_abi/src/extern_abi.rs7
-rw-r--r--compiler/rustc_abi/src/lib.rs13
3 files changed, 149 insertions, 7 deletions
diff --git a/compiler/rustc_abi/src/canon_abi.rs b/compiler/rustc_abi/src/canon_abi.rs
new file mode 100644
index 00000000000..2cf7648a859
--- /dev/null
+++ b/compiler/rustc_abi/src/canon_abi.rs
@@ -0,0 +1,136 @@
+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,
+
+    /// 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 {
+        self.to_erased_extern_abi().as_str().fmt(f)
+    }
+}
+
+impl CanonAbi {
+    /// convert to the ExternAbi that *shares a string* with this CanonAbi
+    ///
+    /// A target-insensitive mapping of CanonAbi to ExternAbi, convenient for "forwarding" impls.
+    /// Importantly, the set of CanonAbi values is a logical *subset* of ExternAbi values,
+    /// so this is injective: if you take an ExternAbi to a CanonAbi and back, you have lost data.
+    const fn to_erased_extern_abi(self) -> ExternAbi {
+        match self {
+            CanonAbi::C => ExternAbi::C { unwind: false },
+            CanonAbi::Rust => ExternAbi::Rust,
+            CanonAbi::RustCold => ExternAbi::RustCold,
+            CanonAbi::Arm(arm_call) => match arm_call {
+                ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },
+                ArmCall::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall,
+                ArmCall::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry,
+            },
+            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 },
+            },
+        }
+    }
+}
+
+/// 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,
+}
diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs
index 55f4845d216..c48920e5f1b 100644
--- a/compiler/rustc_abi/src/extern_abi.rs
+++ b/compiler/rustc_abi/src/extern_abi.rs
@@ -7,6 +7,8 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd};
 #[cfg(feature = "nightly")]
 use rustc_macros::{Decodable, Encodable};
 
+use crate::AbiFromStrErr;
+
 #[cfg(test)]
 mod tests;
 
@@ -99,11 +101,6 @@ macro_rules! abi_impls {
     }
 }
 
-#[derive(Debug)]
-pub enum AbiFromStrErr {
-    Unknown,
-}
-
 abi_impls! {
     ExternAbi = {
             C { unwind: false } =><= "C",
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 59b74d29221..b806d0aba31 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -55,13 +55,14 @@ use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_Generic};
 
 mod callconv;
+mod canon_abi;
+mod extern_abi;
 mod layout;
 #[cfg(test)]
 mod tests;
 
-mod extern_abi;
-
 pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
+pub use canon_abi::{ArmCall, CanonAbi, InterruptKind, X86Call};
 pub use extern_abi::{ExternAbi, all_names};
 #[cfg(feature = "nightly")]
 pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
@@ -1895,3 +1896,11 @@ pub enum StructKind {
     /// A univariant, but with a prefix of an arbitrary size & alignment (e.g., enum tag).
     Prefixed(Size, Align),
 }
+
+#[derive(Clone, Debug)]
+pub enum AbiFromStrErr {
+    /// not a known ABI
+    Unknown,
+    /// no "-unwind" variant can be used here
+    NoExplicitUnwind,
+}