compiler: compare and hash ExternAbi like its string

Directly map each ExternAbi variant to its string and back again.
This has a few advantages:
- By making the ABIs compare equal to their strings, we can easily
  lexicographically sort them and use that sorted slice at runtime.
- We no longer need a workaround to make sure the hashes remain stable,
  as they already naturally are (by being the hashes of unique strings).
- The compiler can carry around less &str wide pointers
This commit is contained in:
Jubilee Young 2025-02-10 03:57:32 -08:00
parent 038c183d5f
commit 8abff35b41
6 changed files with 144 additions and 25 deletions

View file

@ -1,15 +1,20 @@
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::str::FromStr;
#[cfg(feature = "nightly")]
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd};
#[cfg(feature = "nightly")]
use rustc_macros::{Decodable, Encodable};
#[cfg(test)]
mod tests;
use ExternAbi as Abi;
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)]
#[cfg_attr(feature = "nightly", derive(HashStable_Generic, Encodable, Decodable))]
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "nightly", derive(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
@ -69,7 +74,123 @@ pub enum ExternAbi {
RiscvInterruptS,
}
impl Abi {
macro_rules! abi_impls {
($e_name:ident = {
$($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)*
}) => {
impl $e_name {
pub const ALL_VARIANTS: &[Self] = &[
$($e_name::$variant $({ unwind: $uw })*,)*
];
pub const fn as_str(&self) -> &'static str {
match self {
$($e_name::$variant $( { unwind: $uw } )* => $tok,)*
}
}
}
impl ::core::str::FromStr for $e_name {
type Err = AbiFromStrErr;
fn from_str(s: &str) -> Result<$e_name, Self::Err> {
match s {
$($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*
_ => Err(AbiFromStrErr::Unknown),
}
}
}
}
}
pub enum AbiFromStrErr {
Unknown,
}
abi_impls! {
ExternAbi = {
C { unwind: false } =><= "C",
CCmseNonSecureCall =><= "C-cmse-nonsecure-call",
CCmseNonSecureEntry =><= "C-cmse-nonsecure-entry",
C { unwind: true } =><= "C-unwind",
Rust =><= "Rust",
Aapcs { unwind: false } =><= "aapcs",
Aapcs { unwind: true } =><= "aapcs-unwind",
AvrInterrupt =><= "avr-interrupt",
AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",
Cdecl { unwind: false } =><= "cdecl",
Cdecl { unwind: true } =><= "cdecl-unwind",
EfiApi =><= "efiapi",
Fastcall { unwind: false } =><= "fastcall",
Fastcall { unwind: true } =><= "fastcall-unwind",
GpuKernel =><= "gpu-kernel",
Msp430Interrupt =><= "msp430-interrupt",
PtxKernel =><= "ptx-kernel",
RiscvInterruptM =><= "riscv-interrupt-m",
RiscvInterruptS =><= "riscv-interrupt-s",
RustCall =><= "rust-call",
RustCold =><= "rust-cold",
RustIntrinsic =><= "rust-intrinsic",
Stdcall { unwind: false } =><= "stdcall",
Stdcall { unwind: true } =><= "stdcall-unwind",
System { unwind: false } =><= "system",
System { unwind: true } =><= "system-unwind",
SysV64 { unwind: false } =><= "sysv64",
SysV64 { unwind: true } =><= "sysv64-unwind",
Thiscall { unwind: false } =><= "thiscall",
Thiscall { unwind: true } =><= "thiscall-unwind",
Unadjusted =><= "unadjusted",
Vectorcall { unwind: false } =><= "vectorcall",
Vectorcall { unwind: true } =><= "vectorcall-unwind",
Win64 { unwind: false } =><= "win64",
Win64 { unwind: true } =><= "win64-unwind",
X86Interrupt =><= "x86-interrupt",
}
}
impl Ord for ExternAbi {
fn cmp(&self, rhs: &Self) -> Ordering {
self.as_str().cmp(rhs.as_str())
}
}
impl PartialOrd for ExternAbi {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
Some(self.cmp(rhs))
}
}
impl PartialEq for ExternAbi {
fn eq(&self, rhs: &Self) -> bool {
self.cmp(rhs) == Ordering::Equal
}
}
impl Eq for ExternAbi {}
impl Hash for ExternAbi {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
// double-assurance of a prefix breaker
u32::from_be_bytes(*b"ABI\0").hash(state);
}
}
#[cfg(feature = "nightly")]
impl<C> HashStable<C> for ExternAbi {
#[inline]
fn hash_stable(&self, _: &mut C, hasher: &mut StableHasher) {
Hash::hash(self, hasher);
}
}
#[cfg(feature = "nightly")]
impl StableOrd for ExternAbi {
const CAN_USE_UNSTABLE_SORT: bool = true;
// because each ABI is hashed like a string, there is no possible instability
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl ExternAbi {
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.
@ -145,15 +266,11 @@ pub const AbiDatas: &[AbiData] = &[
pub struct AbiUnsupported {}
/// 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(|| AbiUnsupported {})
ExternAbi::from_str(name).map_err(|_| AbiUnsupported {})
}
pub fn all_names() -> Vec<&'static str> {
AbiDatas.iter().map(|d| d.name).collect()
ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect()
}
impl Abi {
@ -229,8 +346,8 @@ impl Abi {
}
}
impl fmt::Display for Abi {
impl fmt::Display for ExternAbi {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"{}\"", self.name())
write!(f, "\"{}\"", self.as_str())
}
}

View file

@ -27,3 +27,11 @@ fn indices_are_correct() {
assert_eq!(i, abi_data.abi.index());
}
}
#[test]
fn guarantee_lexicographic_ordering() {
let abis = ExternAbi::ALL_VARIANTS;
let mut sorted_abis = abis.to_vec();
sorted_abis.sort_unstable();
assert_eq!(abis, sorted_abis);
}

View file

@ -54,17 +54,12 @@ enum GateReason {
impl fmt::Display for UnstableAbi {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { abi, .. } = self;
let name = abi.to_string();
let name = name.trim_matches('"');
match self.explain {
GateReason::Experimental => {
write!(f, r#"the extern "{name}" ABI is experimental and subject to change"#)
write!(f, "the extern {abi} ABI is experimental and subject to change")
}
GateReason::ImplDetail => {
write!(
f,
r#"the extern "{name}" ABI is an implementation detail and perma-unstable"#
)
write!(f, "the extern {abi} ABI is an implementation detail and perma-unstable")
}
}
}

View file

@ -747,8 +747,7 @@ fn print_crate_info(
}
}
CallingConventions => {
let mut calling_conventions = rustc_abi::all_names();
calling_conventions.sort_unstable();
let calling_conventions = rustc_abi::all_names();
println_info!("{}", calling_conventions.join("\n"));
}
RelocationModels

View file

@ -1,10 +1,10 @@
error: symbol-name(_ZN5basic4main17h144191e1523a280eE)
error: symbol-name(_ZN5basic4main17hc88b9d80a69d119aE)
--> $DIR/basic.rs:8:1
|
LL | #[rustc_symbol_name]
| ^^^^^^^^^^^^^^^^^^^^
error: demangling(basic::main::h144191e1523a280e)
error: demangling(basic::main::hc88b9d80a69d119a)
--> $DIR/basic.rs:8:1
|
LL | #[rustc_symbol_name]

View file

@ -1,10 +1,10 @@
error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h71f988fda3b6b180E)
error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17hbddb77d6f71afb32E)
--> $DIR/issue-60925.rs:21:9
|
LL | #[rustc_symbol_name]
| ^^^^^^^^^^^^^^^^^^^^
error: demangling(issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo::h71f988fda3b6b180)
error: demangling(issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo::hbddb77d6f71afb32)
--> $DIR/issue-60925.rs:21:9
|
LL | #[rustc_symbol_name]