refactor(lexer): order variants of DynCpu per architecture (#28774)

Define `DynCpu` separately for each architecture, so its variants can be in a different order on each. `Generic` is always first, then the SIMD feature set which the platform has, then the variants which can't exist on the platform:

- x86_64: `Generic`, `Avx2`, `Neon`.
- aarch64: `Generic`, `Neon`, `Avx2`.
- Other platforms: `Generic`, `Avx2`, `Neon`.

`Generic` is always 0, and the platform's SIMD feature set is 1. Uninhabited variants don't exist at runtime, so `DynCpu` is like a `bool`. On aarch64 with Neon enabled at compile time, and on other platforms, only one variant can exist, so `DynCpu` is zero-sized.

Each variant which can't exist on the platform is `#[expect(dead_code)]`, with no `cfg_attr` needed.

This alters codegen slightly in a few places, but those changes are entirely incidental - same instructions but in a different order, and only in code which only runs once per lex.
This commit is contained in:
overlookmotel committed 2026-11-03 18:02:35 +00:00
1 parent 84a5cd9906
commit f8fa6b2a03
1 file changed
+64 -14
+64 -14
View File
@@ -119,6 +119,18 @@ trait CpuPrivate {
const DYN_CPU: Option<DynCpu>;
}
// `DynCpu` is defined differently for each architecture.
//
// All variants exist on all architectures, but variants impossible on each architecture
// are uninhabited. This allows code for all architectures to be compiled and linted
// on any machine, and Rust Analyzer sees all the code for all archs "as one".
// But the uninhabited variants allow compiler to apply dead code removal to code paths
// which aren't for the architecture crate is being compiled for.
//
// Variants are ordered so that `Generic` is always 0, and the SIMD feature set
// available on the platform is 1. Uninhabited variants don't exist at runtime,
// so this type behaves like a `bool`.
/// Enum representing a CPU feature set.
///
/// Used for runtime feature detection.
@@ -127,26 +139,64 @@ trait CpuPrivate {
/// via target feature detection.
///
/// [`detect`]: Self::detect
#[cfg(target_arch = "x86_64")]
#[derive(Clone, Copy, Debug)]
pub enum DynCpu {
/// AVX2 feature set.
/// Exists on all platforms, but can only be created on x86_64. See [`Avx2`].
#[cfg_attr(not(target_arch = "x86_64"), expect(dead_code))]
Avx2(Avx2),
/// Generic feature set.
/// Exists on all platforms, but can never be created on aarch64
/// when Neon is enabled at compile time. See [`Generic`].
#[cfg_attr(all(target_arch = "aarch64", target_feature = "neon"), expect(dead_code))]
Generic(Generic),
// `Neon` is last. rustc numbers variants in the order they're declared, uninhabited ones too.
// So on x86_64, where `Neon` is uninhabited, the 2 variants which can exist are 0 and 1,
// like a `bool`.
/// Neon feature set.
/// Exists on all platforms, but can only be created on aarch64. See [`Neon`].
#[cfg_attr(not(target_arch = "aarch64"), expect(dead_code))]
Avx2(Avx2),
#[expect(dead_code)]
Neon(Neon),
}
/// Enum representing a CPU feature set.
///
/// Used for runtime feature detection.
///
/// Implements [`Cpu`] and also provides a [`detect`] method which initializes [`DynCpu`]
/// via target feature detection.
///
/// [`detect`]: Self::detect
#[cfg(target_arch = "aarch64")]
#[derive(Clone, Copy, Debug)]
pub enum DynCpu {
// Can never be created on aarch64 when Neon is enabled at compile time. See `Generic`.
#[cfg_attr(target_feature = "neon", expect(dead_code))]
Generic(Generic),
Neon(Neon),
#[expect(dead_code)]
Avx2(Avx2),
}
/// Enum representing a CPU feature set.
///
/// Used for runtime feature detection.
///
/// Implements [`Cpu`] and also provides a [`detect`] method which initializes [`DynCpu`]
/// via target feature detection.
///
/// [`detect`]: Self::detect
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
#[derive(Clone, Copy, Debug)]
pub enum DynCpu {
Generic(Generic),
#[expect(dead_code)]
Avx2(Avx2),
#[expect(dead_code)]
Neon(Neon),
}
/// On aarch64 with neon enabled, [`DynCpu`] has only 1 inhabited variant, so is a ZST.
/// Ditto when arch is not x86_64 or aarch64.
/// On all other platforms it's 1 byte.
const _: () = {
let expected_size = cfg_select! {
all(target_arch = "aarch64", target_feature = "neon") => 0,
not(any(target_arch = "x86_64", target_arch = "aarch64")) => 0,
_ => 1,
};
assert!(size_of::<DynCpu>() == expected_size);
};
impl Cpu for DynCpu {}
impl CpuPrivate for DynCpu {