compiler: Lift rustc_target::spec::abi::Abi
to rustc_abi::ExternAbi
This commit is contained in:
parent
4add5e4211
commit
eca17022ef
6 changed files with 24 additions and 2 deletions
|
@ -9,9 +9,11 @@ bitflags = "2.4.1"
|
|||
rand = { version = "0.8.4", default-features = false, optional = true }
|
||||
rand_xoshiro = { version = "0.6.0", optional = true }
|
||||
rustc_data_structures = { path = "../rustc_data_structures", optional = true }
|
||||
rustc_feature = { path = "../rustc_feature", optional = true }
|
||||
rustc_index = { path = "../rustc_index", default-features = false }
|
||||
rustc_macros = { path = "../rustc_macros", optional = true }
|
||||
rustc_serialize = { path = "../rustc_serialize", optional = true }
|
||||
rustc_span = { path = "../rustc_span", optional = true }
|
||||
tracing = "0.1"
|
||||
# tidy-alphabetical-end
|
||||
|
||||
|
@ -22,8 +24,10 @@ default = ["nightly", "randomize"]
|
|||
# without depending on rustc_data_structures, rustc_macros and rustc_serialize
|
||||
nightly = [
|
||||
"dep:rustc_data_structures",
|
||||
"dep:rustc_feature",
|
||||
"dep:rustc_macros",
|
||||
"dep:rustc_serialize",
|
||||
"dep:rustc_span",
|
||||
"rustc_index/nightly",
|
||||
]
|
||||
randomize = ["dep:rand", "dep:rand_xoshiro", "nightly"]
|
||||
|
|
335
compiler/rustc_abi/src/extern_abi/mod.rs
Normal file
335
compiler/rustc_abi/src/extern_abi/mod.rs
Normal file
|
@ -0,0 +1,335 @@
|
|||
use std::fmt;
|
||||
|
||||
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use ExternAbi as Abi;
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)]
|
||||
#[derive(HashStable_Generic, Encodable, Decodable)]
|
||||
pub enum ExternAbi {
|
||||
// Some of the ABIs come first because every time we add a new ABI, we have to re-bless all the
|
||||
// hashing tests. These are used in many places, so giving them stable values reduces test
|
||||
// churn. The specific values are meaningless.
|
||||
Rust,
|
||||
C {
|
||||
unwind: bool,
|
||||
},
|
||||
Cdecl {
|
||||
unwind: bool,
|
||||
},
|
||||
Stdcall {
|
||||
unwind: bool,
|
||||
},
|
||||
Fastcall {
|
||||
unwind: bool,
|
||||
},
|
||||
Vectorcall {
|
||||
unwind: bool,
|
||||
},
|
||||
Thiscall {
|
||||
unwind: bool,
|
||||
},
|
||||
Aapcs {
|
||||
unwind: bool,
|
||||
},
|
||||
Win64 {
|
||||
unwind: bool,
|
||||
},
|
||||
SysV64 {
|
||||
unwind: bool,
|
||||
},
|
||||
PtxKernel,
|
||||
Msp430Interrupt,
|
||||
X86Interrupt,
|
||||
EfiApi,
|
||||
AvrInterrupt,
|
||||
AvrNonBlockingInterrupt,
|
||||
CCmseNonSecureCall,
|
||||
CCmseNonSecureEntry,
|
||||
System {
|
||||
unwind: bool,
|
||||
},
|
||||
RustIntrinsic,
|
||||
RustCall,
|
||||
/// *Not* a stable ABI, just directly use the Rust types to describe the ABI for LLVM. Even
|
||||
/// normally ABI-compatible Rust types can become ABI-incompatible with this ABI!
|
||||
Unadjusted,
|
||||
/// For things unlikely to be called, where reducing register pressure in
|
||||
/// `extern "Rust"` callers is worth paying extra cost in the callee.
|
||||
/// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
|
||||
RustCold,
|
||||
RiscvInterruptM,
|
||||
RiscvInterruptS,
|
||||
}
|
||||
|
||||
impl Abi {
|
||||
pub fn supports_varargs(self) -> bool {
|
||||
// * C and Cdecl obviously support varargs.
|
||||
// * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.
|
||||
// * EfiApi is based on Win64 or C, so it also supports it.
|
||||
// * System falls back to C for functions with varargs.
|
||||
//
|
||||
// * Stdcall does not, because it would be impossible for the callee to clean
|
||||
// up the arguments. (callee doesn't know how many arguments are there)
|
||||
// * Same for Fastcall, Vectorcall and Thiscall.
|
||||
// * Other calling conventions are related to hardware or the compiler itself.
|
||||
match self {
|
||||
Self::C { .. }
|
||||
| Self::Cdecl { .. }
|
||||
| Self::System { .. }
|
||||
| Self::Aapcs { .. }
|
||||
| Self::Win64 { .. }
|
||||
| Self::SysV64 { .. }
|
||||
| Self::EfiApi => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct AbiData {
|
||||
abi: Abi,
|
||||
|
||||
/// Name of this ABI as we like it called.
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
const AbiDatas: &[AbiData] = &[
|
||||
AbiData { abi: Abi::Rust, name: "Rust" },
|
||||
AbiData { abi: Abi::C { unwind: false }, name: "C" },
|
||||
AbiData { abi: Abi::C { unwind: true }, name: "C-unwind" },
|
||||
AbiData { abi: Abi::Cdecl { unwind: false }, name: "cdecl" },
|
||||
AbiData { abi: Abi::Cdecl { unwind: true }, name: "cdecl-unwind" },
|
||||
AbiData { abi: Abi::Stdcall { unwind: false }, name: "stdcall" },
|
||||
AbiData { abi: Abi::Stdcall { unwind: true }, name: "stdcall-unwind" },
|
||||
AbiData { abi: Abi::Fastcall { unwind: false }, name: "fastcall" },
|
||||
AbiData { abi: Abi::Fastcall { unwind: true }, name: "fastcall-unwind" },
|
||||
AbiData { abi: Abi::Vectorcall { unwind: false }, name: "vectorcall" },
|
||||
AbiData { abi: Abi::Vectorcall { unwind: true }, name: "vectorcall-unwind" },
|
||||
AbiData { abi: Abi::Thiscall { unwind: false }, name: "thiscall" },
|
||||
AbiData { abi: Abi::Thiscall { unwind: true }, name: "thiscall-unwind" },
|
||||
AbiData { abi: Abi::Aapcs { unwind: false }, name: "aapcs" },
|
||||
AbiData { abi: Abi::Aapcs { unwind: true }, name: "aapcs-unwind" },
|
||||
AbiData { abi: Abi::Win64 { unwind: false }, name: "win64" },
|
||||
AbiData { abi: Abi::Win64 { unwind: true }, name: "win64-unwind" },
|
||||
AbiData { abi: Abi::SysV64 { unwind: false }, name: "sysv64" },
|
||||
AbiData { abi: Abi::SysV64 { unwind: true }, name: "sysv64-unwind" },
|
||||
AbiData { abi: Abi::PtxKernel, name: "ptx-kernel" },
|
||||
AbiData { abi: Abi::Msp430Interrupt, name: "msp430-interrupt" },
|
||||
AbiData { abi: Abi::X86Interrupt, name: "x86-interrupt" },
|
||||
AbiData { abi: Abi::EfiApi, name: "efiapi" },
|
||||
AbiData { abi: Abi::AvrInterrupt, name: "avr-interrupt" },
|
||||
AbiData { abi: Abi::AvrNonBlockingInterrupt, name: "avr-non-blocking-interrupt" },
|
||||
AbiData { abi: Abi::CCmseNonSecureCall, name: "C-cmse-nonsecure-call" },
|
||||
AbiData { abi: Abi::CCmseNonSecureEntry, name: "C-cmse-nonsecure-entry" },
|
||||
AbiData { abi: Abi::System { unwind: false }, name: "system" },
|
||||
AbiData { abi: Abi::System { unwind: true }, name: "system-unwind" },
|
||||
AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic" },
|
||||
AbiData { abi: Abi::RustCall, name: "rust-call" },
|
||||
AbiData { abi: Abi::Unadjusted, name: "unadjusted" },
|
||||
AbiData { abi: Abi::RustCold, name: "rust-cold" },
|
||||
AbiData { abi: Abi::RiscvInterruptM, name: "riscv-interrupt-m" },
|
||||
AbiData { abi: Abi::RiscvInterruptS, name: "riscv-interrupt-s" },
|
||||
];
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum AbiUnsupported {
|
||||
Unrecognized,
|
||||
Reason { explain: &'static str },
|
||||
}
|
||||
|
||||
/// Returns the ABI with the given name (if any).
|
||||
pub fn lookup(name: &str) -> Result<Abi, AbiUnsupported> {
|
||||
AbiDatas.iter().find(|abi_data| name == abi_data.name).map(|&x| x.abi).ok_or_else(|| match name {
|
||||
"riscv-interrupt" => AbiUnsupported::Reason {
|
||||
explain: "please use one of riscv-interrupt-m or riscv-interrupt-s for machine- or supervisor-level interrupts, respectively",
|
||||
},
|
||||
"riscv-interrupt-u" => AbiUnsupported::Reason {
|
||||
explain: "user-mode interrupt handlers have been removed from LLVM pending standardization, see: https://reviews.llvm.org/D149314",
|
||||
},
|
||||
"wasm" => AbiUnsupported::Reason {
|
||||
explain: "non-standard wasm ABI is no longer supported",
|
||||
},
|
||||
|
||||
_ => AbiUnsupported::Unrecognized,
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
pub fn all_names() -> Vec<&'static str> {
|
||||
AbiDatas.iter().map(|d| d.name).collect()
|
||||
}
|
||||
|
||||
pub fn enabled_names(features: &rustc_feature::Features, span: Span) -> Vec<&'static str> {
|
||||
AbiDatas
|
||||
.iter()
|
||||
.map(|d| d.name)
|
||||
.filter(|name| is_enabled(features, span, name).is_ok())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub enum AbiDisabled {
|
||||
Unstable { feature: Symbol, explain: &'static str },
|
||||
Unrecognized,
|
||||
}
|
||||
|
||||
pub fn is_enabled(
|
||||
features: &rustc_feature::Features,
|
||||
span: Span,
|
||||
name: &str,
|
||||
) -> Result<(), AbiDisabled> {
|
||||
let s = is_stable(name);
|
||||
if let Err(AbiDisabled::Unstable { feature, .. }) = s {
|
||||
if features.enabled(feature) || span.allows_unstable(feature) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
pub fn is_stable(name: &str) -> Result<(), AbiDisabled> {
|
||||
match name {
|
||||
// Stable
|
||||
"Rust" | "C" | "C-unwind" | "cdecl" | "cdecl-unwind" | "stdcall" | "stdcall-unwind"
|
||||
| "fastcall" | "fastcall-unwind" | "aapcs" | "aapcs-unwind" | "win64" | "win64-unwind"
|
||||
| "sysv64" | "sysv64-unwind" | "system" | "system-unwind" | "efiapi" | "thiscall"
|
||||
| "thiscall-unwind" => Ok(()),
|
||||
"rust-intrinsic" => Err(AbiDisabled::Unstable {
|
||||
feature: sym::intrinsics,
|
||||
explain: "intrinsics are subject to change",
|
||||
}),
|
||||
"vectorcall" => Err(AbiDisabled::Unstable {
|
||||
feature: sym::abi_vectorcall,
|
||||
explain: "vectorcall is experimental and subject to change",
|
||||
}),
|
||||
"vectorcall-unwind" => Err(AbiDisabled::Unstable {
|
||||
feature: sym::abi_vectorcall,
|
||||
explain: "vectorcall-unwind ABI is experimental and subject to change",
|
||||
}),
|
||||
"rust-call" => Err(AbiDisabled::Unstable {
|
||||
feature: sym::unboxed_closures,
|
||||
explain: "rust-call ABI is subject to change",
|
||||
}),
|
||||
"rust-cold" => Err(AbiDisabled::Unstable {
|
||||
feature: sym::rust_cold_cc,
|
||||
explain: "rust-cold is experimental and subject to change",
|
||||
}),
|
||||
"ptx-kernel" => Err(AbiDisabled::Unstable {
|
||||
feature: sym::abi_ptx,
|
||||
explain: "PTX ABIs are experimental and subject to change",
|
||||
}),
|
||||
"unadjusted" => Err(AbiDisabled::Unstable {
|
||||
feature: sym::abi_unadjusted,
|
||||
explain: "unadjusted ABI is an implementation detail and perma-unstable",
|
||||
}),
|
||||
"msp430-interrupt" => Err(AbiDisabled::Unstable {
|
||||
feature: sym::abi_msp430_interrupt,
|
||||
explain: "msp430-interrupt ABI is experimental and subject to change",
|
||||
}),
|
||||
"x86-interrupt" => Err(AbiDisabled::Unstable {
|
||||
feature: sym::abi_x86_interrupt,
|
||||
explain: "x86-interrupt ABI is experimental and subject to change",
|
||||
}),
|
||||
"avr-interrupt" | "avr-non-blocking-interrupt" => Err(AbiDisabled::Unstable {
|
||||
feature: sym::abi_avr_interrupt,
|
||||
explain: "avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change",
|
||||
}),
|
||||
"riscv-interrupt-m" | "riscv-interrupt-s" => Err(AbiDisabled::Unstable {
|
||||
feature: sym::abi_riscv_interrupt,
|
||||
explain: "riscv-interrupt ABIs are experimental and subject to change",
|
||||
}),
|
||||
"C-cmse-nonsecure-call" => Err(AbiDisabled::Unstable {
|
||||
feature: sym::abi_c_cmse_nonsecure_call,
|
||||
explain: "C-cmse-nonsecure-call ABI is experimental and subject to change",
|
||||
}),
|
||||
"C-cmse-nonsecure-entry" => Err(AbiDisabled::Unstable {
|
||||
feature: sym::cmse_nonsecure_entry,
|
||||
explain: "C-cmse-nonsecure-entry ABI is experimental and subject to change",
|
||||
}),
|
||||
_ => Err(AbiDisabled::Unrecognized),
|
||||
}
|
||||
}
|
||||
|
||||
impl Abi {
|
||||
/// Default ABI chosen for `extern fn` declarations without an explicit ABI.
|
||||
pub const FALLBACK: Abi = Abi::C { unwind: false };
|
||||
|
||||
#[inline]
|
||||
pub fn index(self) -> usize {
|
||||
// N.B., this ordering MUST match the AbiDatas array above.
|
||||
// (This is ensured by the test indices_are_correct().)
|
||||
use Abi::*;
|
||||
let i = match self {
|
||||
// Cross-platform ABIs
|
||||
Rust => 0,
|
||||
C { unwind: false } => 1,
|
||||
C { unwind: true } => 2,
|
||||
// Platform-specific ABIs
|
||||
Cdecl { unwind: false } => 3,
|
||||
Cdecl { unwind: true } => 4,
|
||||
Stdcall { unwind: false } => 5,
|
||||
Stdcall { unwind: true } => 6,
|
||||
Fastcall { unwind: false } => 7,
|
||||
Fastcall { unwind: true } => 8,
|
||||
Vectorcall { unwind: false } => 9,
|
||||
Vectorcall { unwind: true } => 10,
|
||||
Thiscall { unwind: false } => 11,
|
||||
Thiscall { unwind: true } => 12,
|
||||
Aapcs { unwind: false } => 13,
|
||||
Aapcs { unwind: true } => 14,
|
||||
Win64 { unwind: false } => 15,
|
||||
Win64 { unwind: true } => 16,
|
||||
SysV64 { unwind: false } => 17,
|
||||
SysV64 { unwind: true } => 18,
|
||||
PtxKernel => 19,
|
||||
Msp430Interrupt => 20,
|
||||
X86Interrupt => 21,
|
||||
EfiApi => 22,
|
||||
AvrInterrupt => 23,
|
||||
AvrNonBlockingInterrupt => 24,
|
||||
CCmseNonSecureCall => 25,
|
||||
CCmseNonSecureEntry => 26,
|
||||
// Cross-platform ABIs
|
||||
System { unwind: false } => 27,
|
||||
System { unwind: true } => 28,
|
||||
RustIntrinsic => 29,
|
||||
RustCall => 30,
|
||||
Unadjusted => 31,
|
||||
RustCold => 32,
|
||||
RiscvInterruptM => 33,
|
||||
RiscvInterruptS => 34,
|
||||
};
|
||||
debug_assert!(
|
||||
AbiDatas
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, AbiData { abi, .. })| *abi == self)
|
||||
.map(|(index, _)| index)
|
||||
.expect("abi variant has associated data")
|
||||
== i,
|
||||
"Abi index did not match `AbiDatas` ordering"
|
||||
);
|
||||
i
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn data(self) -> &'static AbiData {
|
||||
&AbiDatas[self.index()]
|
||||
}
|
||||
|
||||
pub fn name(self) -> &'static str {
|
||||
self.data().name
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Abi {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "\"{}\"", self.name())
|
||||
}
|
||||
}
|
29
compiler/rustc_abi/src/extern_abi/tests.rs
Normal file
29
compiler/rustc_abi/src/extern_abi/tests.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
use std::assert_matches::assert_matches;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[test]
|
||||
fn lookup_Rust() {
|
||||
let abi = lookup("Rust");
|
||||
assert!(abi.is_ok() && abi.unwrap().data().name == "Rust");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_cdecl() {
|
||||
let abi = lookup("cdecl");
|
||||
assert!(abi.is_ok() && abi.unwrap().data().name == "cdecl");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup_baz() {
|
||||
let abi = lookup("baz");
|
||||
assert_matches!(abi, Err(AbiUnsupported::Unrecognized));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indices_are_correct() {
|
||||
for (i, abi_data) in AbiDatas.iter().enumerate() {
|
||||
assert_eq!(i, abi_data.abi.index());
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
// tidy-alphabetical-start
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
#![cfg_attr(feature = "nightly", doc(rust_logo))]
|
||||
#![cfg_attr(feature = "nightly", feature(assert_matches))]
|
||||
#![cfg_attr(feature = "nightly", feature(rustc_attrs))]
|
||||
#![cfg_attr(feature = "nightly", feature(rustdoc_internals))]
|
||||
#![cfg_attr(feature = "nightly", feature(step_trait))]
|
||||
|
@ -28,8 +29,15 @@ mod layout;
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
mod extern_abi;
|
||||
|
||||
pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use extern_abi::{
|
||||
AbiDisabled, AbiUnsupported, ExternAbi, all_names, enabled_names, is_enabled, is_stable, lookup,
|
||||
};
|
||||
#[cfg(feature = "nightly")]
|
||||
pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
|
||||
pub use layout::{LayoutCalculator, LayoutCalculatorError};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue