From 6d5be86ea159a90101f5a4e3815dbda87208ec9e Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Tue, 3 Nov 2026 18:02:32 +0000 Subject: [PATCH] refactor(lexer): make CPU types and all `DynCpu` variants exist on all platforms (#28769) Pure refactor - no change to codegen. Preparatory step for compiling all of the lexer's code on all platforms (#28772). Previously, `Avx2` only existed on x86_64, and `Neon` only on aarch64, and the variants of `DynCpu` had `#[cfg]` attributes to match. `Generic` existed on all platforms, but was uninhabited on aarch64 with Neon enabled. Make `Avx2` and `Neon` work the same way as `Generic`. All these types now exists on every platform. Each has a private field which is `()` on its own platform, and `Never` (an enum with no variants) everywhere else: ```rust pub struct Avx2( cfg_select! { target_arch = "x86_64" => (), _ => Never, }, ); ``` On platforms which can't support the CPU feature set, the type is uninhabited, so it can never be created, and the compiler removes code which handles it. `DynCpu` has all 3 variants on every platform. Variants which can't exist on a platform are `#[expect(dead_code)]` there. The field is private, so only code in the `cpu` module knows that a type is uninhabited. A `match` anywhere else needs an arm for every variant, including ones which can't exist. So `cpu_select!` and `#[cpu_multiversion]` add arms for them, e.g. `DynCpu::Neon(_) => unreachable!()` on x86_64. --- crates/oxc_lexer/src/cpu/mod.rs | 305 ++++++++++---------- crates/oxc_lexer/src/cpu/select_macro.rs | 20 +- crates/oxc_lexer/src/cpu/tests.rs | 6 + crates/oxc_lexer_macros/src/cpu.rs | 11 +- crates/oxc_lexer_macros/src/lib.rs | 11 + crates/oxc_lexer_macros/src/multiversion.rs | 26 +- 6 files changed, 213 insertions(+), 166 deletions(-) diff --git a/crates/oxc_lexer/src/cpu/mod.rs b/crates/oxc_lexer/src/cpu/mod.rs index 7cea62bbad..6f474303d6 100644 --- a/crates/oxc_lexer/src/cpu/mod.rs +++ b/crates/oxc_lexer/src/cpu/mod.rs @@ -1,8 +1,11 @@ //! Types representing CPU feature sets, used to choose between SIMD and generic implementations. //! -//! - `Avx2`: x86_64 with AVX2, BMI2 and POPCNT. -//! - `Neon`: aarch64 with Neon. -//! - [`Generic`]: No SIMD. Exists on all platforms. +//! - [`Avx2`]: x86_64 with AVX2, BMI2 and POPCNT. +//! - [`Neon`]: aarch64 with Neon. +//! - [`Generic`]: No SIMD. +//! +//! All 3 types exist on all platforms. On a platform where a feature set is never used, +//! its type is uninhabited - it can never be created. //! //! [`DynCpu`] is an enum of these types. [`DynCpu::detect`] returns the best one available. //! @@ -26,22 +29,24 @@ //! can do either a compile-time or runtime features check, depending on whether it's called with //! a concrete type (e.g. `Avx2`) or [`DynCpu`]. //! -//! `Avx2`, `Neon`, and [`Generic`] types are all zero-sized, so can be passed between functions +//! [`Avx2`], [`Neon`] and [`Generic`] types are all zero-sized, so can be passed between functions //! without any runtime cost. They operate at the level of the type system only. //! //! # SAFETY //! //! These types are unforgeable. //! -//! * Code outside this file can't create them. -//! * Each has a private field, and only 3 places create them: -//! - `DynCpu::detect` only creates an `Avx2` if that feature set is available. -//! - [`BASELINE`] is only a `Neon` when Neon is enabled at compile time, which guarantees it's available. +//! * Each has a private field, so code outside this module can't create them. +//! * Only 3 places create them: +//! - `DynCpu::detect` only creates an [`Avx2`] if that feature set is available. +//! - [`BASELINE`] is only a [`Neon`] when Neon is enabled at compile time, +//! which guarantees it's available. //! Otherwise it's a [`Generic`], which guarantees nothing. //! - `CpuPrivate::DYN_CPU` holds an instance of the type. //! The trait is private, so only code in this file can read it. //! Only [`to_dyn`] reads it, and it takes an instance of the same type, //! so it only gives one to code which already has one. +//! * On a platform where a type is uninhabited, it can't be created at all. //! * A [`DynCpu`] can only be built from one of these types, so it's unforgeable too. //! * [`Cpu`] trait is sealed, so no other type can implement it. //! @@ -55,7 +60,7 @@ //! [`#[cpu_version]`]: macro@cpu_version //! [`#[cpu_multiversion]`]: macro@cpu_multiversion -use std::mem; +use std::{convert::Infallible as Never, mem}; pub(crate) use oxc_lexer_macros::{cpu_multiversion, cpu_version}; @@ -93,8 +98,8 @@ pub trait Cpu: CpuPrivate + Copy { trait CpuPrivate { /// The [`DynCpu`] which [`to_dyn`] returns for this type. /// - /// `Avx2`, `Neon` and [`Generic`] each have only one value, so this can hold it. - /// `None` for [`DynCpu`] itself, and for [`Generic`] when it's uninhabited. + /// [`Avx2`], [`Neon`] and [`Generic`] each have only one value, so this can hold it. + /// `None` for [`DynCpu`] itself, and for each of them on platforms where it's uninhabited. const DYN_CPU: Option; } @@ -108,16 +113,22 @@ trait CpuPrivate { /// [`detect`]: Self::detect #[derive(Clone, Copy, Debug)] pub enum DynCpu { - /// AVX2 feature set. Only available on x86_64. - #[cfg(target_arch = "x86_64")] + /// 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), - /// Neon feature set. Only available on aarch64. - #[cfg(target_arch = "aarch64")] - Neon(Neon), + /// 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))] + Neon(Neon), } impl Cpu for DynCpu {} @@ -141,158 +152,134 @@ impl CpuPrivate for DynCpu { pub const fn to_dyn(cpu: C) -> DynCpu { match C::DYN_CPU { Some(dyn_cpu) => dyn_cpu, - // SAFETY: `DYN_CPU` is `None` only for `DynCpu`, and for `Generic` when it's uninhabited. + // SAFETY: `DYN_CPU` is `None` only for `DynCpu`, and for CPU types which are uninhabited. // No value of an uninhabited type exists, so `cpu` must be a `DynCpu`. None => unsafe { mem::transmute_copy::(&cpu) }, } } -/// Generic implementation. -mod generic { - use super::*; - - /// Generic CPU feature set. - /// - /// On aarch64 with Neon enabled at compile time, this type is uninhabited - `Generic` can never be created. - /// So [`DynCpu::Generic`] can never exist there, and the compiler removes code which handles it. - /// Every aarch64 target with `std` enables Neon by default, so in practice this applies to all aarch64 builds. - /// - /// The type still exists there, so code which uses it is still compiled and type-checked. - /// That means generic code is still linted, and is visible to rust-analyzer, when working on an ARM Mac. - #[derive(Clone, Copy, Debug)] - pub struct Generic(GenericInner); - - #[cfg(not(all(target_arch = "aarch64", target_feature = "neon")))] - type GenericInner = (); - - // When Neon is enabled at compile time, Neon is always available, so the generic implementation - // is never needed. `Infallible` has no values, which makes `Generic` impossible to create. - #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] - type GenericInner = std::convert::Infallible; - - impl Cpu for Generic {} - - impl CpuPrivate for Generic { - const DYN_CPU: Option = cfg_select! { - all(target_arch = "aarch64", target_feature = "neon") => None, - _ => Some(DynCpu::Generic(GENERIC)), - }; - } - - /// [`Generic`] feature set. - /// - /// Private within this file. Used only to define [`BASELINE`] and `CpuPrivate::DYN_CPU`. - /// Not available on aarch64 with Neon enabled at compile time, where a [`Generic`] can't be created. - #[cfg(not(all(target_arch = "aarch64", target_feature = "neon")))] - pub(super) const GENERIC: Generic = Generic(()); -} - -pub use generic::Generic; - -/// x86_64 features. -/// AVX2 or generic. -#[cfg(target_arch = "x86_64")] -mod x86_64 { - use super::*; - - /// x64_64 AVX2 CPU feature set. - #[derive(Clone, Copy, Debug)] - pub struct Avx2(()); - - impl Cpu for Avx2 {} - - impl CpuPrivate for Avx2 { - const DYN_CPU: Option = Some(DynCpu::Avx2(Avx2(()))); - } - - /// Feature set to use without detecting the CPU's features. - /// - /// Always [`Generic`] on x86_64. - pub const BASELINE: Generic = generic::GENERIC; - - impl DynCpu { - /// Detect CPU feature set for x86_64 platform. - pub fn detect() -> Self { - // Currently a compile-time check. Later on will be replaced by runtime detection. - if cfg!(all( - target_feature = "avx2", - target_feature = "bmi2", - target_feature = "popcnt" - )) { - Avx2(()).to_dyn() - } else { - BASELINE.to_dyn() - } - } - } -} - -#[cfg(target_arch = "x86_64")] -pub use x86_64::{Avx2, BASELINE}; - -/// aarch64 features. -/// Neon or generic. -#[cfg(target_arch = "aarch64")] -mod aarch64 { - use super::*; - - /// aarch64 Neon CPU feature set. - #[derive(Clone, Copy, Debug)] - pub struct Neon(()); - - impl Cpu for Neon {} - - impl CpuPrivate for Neon { - const DYN_CPU: Option = Some(DynCpu::Neon(Neon(()))); - } - - /// Feature set to use without detecting the CPU's features. - /// - /// Neon is enabled at compile time, which guarantees that it's available. - #[cfg(target_feature = "neon")] - pub const BASELINE: Neon = Neon(()); - - /// Feature set to use without detecting the CPU's features. - /// - /// Neon is disabled at compile time, so only the generic feature set is known to be available. - #[cfg(not(target_feature = "neon"))] - pub const BASELINE: Generic = generic::GENERIC; - - impl DynCpu { - /// Detect CPU feature set for aarch64 platform. - pub fn detect() -> Self { - // Neon is always enabled except on `aarch64*-unknown-none-softfloat` platforms - // or when it's specifically disabled by compiling with `-C target-feature=-neon`. - // Either way, it can be detected at compile time. No runtime feature check. - BASELINE.to_dyn() - } - } -} - -#[cfg(target_arch = "aarch64")] -pub use aarch64::{BASELINE, Neon}; - -/// Fallback implementation for other platforms. +/// x86_64 AVX2 CPU feature set. /// -/// Only generic feature set is available. +/// On platforms other than x86_64, this type is uninhabited - `Avx2` can never be created. +/// So [`DynCpu::Avx2`] can never exist there, and the compiler removes code which handles it. +/// +/// The type still exists there, so [`DynCpu`] has the same variants on every platform. +#[derive(Clone, Copy, Debug)] +pub struct Avx2( + cfg_select! { + target_arch = "x86_64" => (), + _ => Never, + }, +); + +impl Cpu for Avx2 {} + +impl CpuPrivate for Avx2 { + const DYN_CPU: Option = cfg_select! { + target_arch = "x86_64" => Some(DynCpu::Avx2(Avx2(()))), + _ => None, + }; +} + +/// aarch64 Neon CPU feature set. +/// +/// On platforms other than aarch64, this type is uninhabited - `Neon` can never be created. +/// So [`DynCpu::Neon`] can never exist there, and the compiler removes code which handles it. +/// +/// The type still exists there, so [`DynCpu`] has the same variants on every platform. +#[derive(Clone, Copy, Debug)] +pub struct Neon( + cfg_select! { + target_arch = "aarch64" => (), + _ => Never, + }, +); + +impl Cpu for Neon {} + +impl CpuPrivate for Neon { + const DYN_CPU: Option = cfg_select! { + target_arch = "aarch64" => Some(DynCpu::Neon(Neon(()))), + _ => None, + }; +} + +/// Generic CPU feature set. +/// +/// On aarch64 with Neon enabled at compile time, this type is uninhabited - `Generic` can never be created. +/// So [`DynCpu::Generic`] can never exist there, and the compiler removes code which handles it. +/// Every aarch64 target with `std` enables Neon by default, so in practice this applies to all aarch64 builds. +/// +/// The type still exists there, so code which uses it is still compiled and type-checked. +/// That means generic code is still linted, and is visible to rust-analyzer, when working on an ARM Mac. +#[derive(Clone, Copy, Debug)] +pub struct Generic( + cfg_select! { + not(all(target_arch = "aarch64", target_feature = "neon")) => (), + _ => Never, + }, +); + +impl Cpu for Generic {} + +impl CpuPrivate for Generic { + const DYN_CPU: Option = cfg_select! { + not(all(target_arch = "aarch64", target_feature = "neon")) => { + Some(DynCpu::Generic(Generic(()))) + } + _ => None, + }; +} + +/// Feature set to use without detecting the CPU's features. +/// +/// Always [`Generic`] on x86_64. +#[cfg(target_arch = "x86_64")] +pub const BASELINE: Generic = Generic(()); + +/// Feature set to use without detecting the CPU's features. +/// +/// Neon is enabled at compile time, which guarantees that it's available. +#[cfg(all(target_arch = "aarch64", target_feature = "neon"))] +pub const BASELINE: Neon = Neon(()); + +/// Feature set to use without detecting the CPU's features. +/// +/// Neon is disabled at compile time, so only the generic feature set is known to be available. +#[cfg(all(target_arch = "aarch64", not(target_feature = "neon")))] +pub const BASELINE: Generic = Generic(()); + +/// Feature set to use without detecting the CPU's features. +/// +/// Only the generic feature set is available on platforms other than x86_64 and aarch64. #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] -mod other { - use super::*; +pub const BASELINE: Generic = Generic(()); - /// Feature set to use without detecting the CPU's features. - /// - /// Only the generic feature set is available on platforms other than x86_64 and aarch64. - pub const BASELINE: Generic = generic::GENERIC; - - impl DynCpu { - /// Detect CPU feature set for other platform. - /// - /// Only generic feature set is available. - pub fn detect() -> Self { +impl DynCpu { + /// Detect CPU feature set. + #[cfg(target_arch = "x86_64")] + pub fn detect() -> Self { + // Currently a compile-time check. Later on will be replaced by runtime detection. + if cfg!(all(target_feature = "avx2", target_feature = "bmi2", target_feature = "popcnt")) { + Avx2(()).to_dyn() + } else { BASELINE.to_dyn() } } -} -#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] -pub use other::BASELINE; + /// Detect CPU feature set. + #[cfg(target_arch = "aarch64")] + pub fn detect() -> Self { + // Neon is always enabled except on `aarch64*-unknown-none-softfloat` platforms + // or when it's specifically disabled by compiling with `-C target-feature=-neon`. + // Either way, it can be detected at compile time. No runtime feature check. + BASELINE.to_dyn() + } + + /// Detect CPU feature set. + #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] + pub fn detect() -> Self { + // Only generic feature set is available + BASELINE.to_dyn() + } +} diff --git a/crates/oxc_lexer/src/cpu/select_macro.rs b/crates/oxc_lexer/src/cpu/select_macro.rs index 7504ea273b..961e43ba16 100644 --- a/crates/oxc_lexer/src/cpu/select_macro.rs +++ b/crates/oxc_lexer/src/cpu/select_macro.rs @@ -3,11 +3,18 @@ /// Arms use normal `match` syntax, but there must be exactly these 3 arms, in this order: /// /// - `Avx2(...)`: Only compiled on x86_64. +/// On other platforms, the arm's body is replaced with `unreachable!()`. /// - `Neon(...)`: Only compiled on aarch64. +/// On other platforms, the arm's body is replaced with `unreachable!()`. /// - `Generic(...)`: Compiled everywhere. /// On aarch64 with Neon enabled at compile time, it's compiled and type-checked, /// but can never run, so the compiler removes it. See [`Generic`]. /// +/// [`DynCpu`] has all 3 variants on every platform. On a platform which doesn't have +/// a SIMD feature set, its type is uninhabited, so its arm can never run. +/// The arm is still needed, because the type's field is private, so outside the module which +/// defines the type, rustc doesn't know that it's uninhabited. +/// /// Each arm binds its CPU feature set type (e.g. `Avx2`), so it can be passed on. /// Fallback arms (e.g. `_ => ...`) and match guards are not allowed. /// @@ -52,9 +59,13 @@ /// /// ```ignore /// match to_dyn(cpu) { +/// #[cfg(not(target_arch = "x86_64"))] +/// DynCpu::Avx2(_) => unreachable!(), /// #[cfg(target_arch = "x86_64")] /// DynCpu::Avx2(cpu) => classify_avx2(self, cpu), /// +/// #[cfg(not(target_arch = "aarch64"))] +/// DynCpu::Neon(_) => unreachable!(), /// #[cfg(target_arch = "aarch64")] /// DynCpu::Neon(cpu) => classify_neon(self, cpu), /// @@ -66,7 +77,9 @@ /// /// Tokens of the arms are consumed one at a time, and copied into `arms`. /// `Avx2(...) =>` is rewritten to `DynCpu::Avx2(...) =>`, with a `#[cfg]` attribute for x86_64. -/// `Neon(...) =>` is rewritten the same way, with a `#[cfg]` attribute for aarch64. +/// Before it goes an arm `DynCpu::Avx2(_) => unreachable!(),` for other platforms. +/// That replaces the body without having to find where it ends. +/// `Neon(...) =>` is rewritten the same way, with `#[cfg]` attributes for aarch64. /// `Generic(...) =>` is rewritten to `DynCpu::Generic(...) =>`, with no `#[cfg]` attribute. /// All other tokens are copied unchanged, so the arm bodies are parsed by the `match` itself. /// @@ -83,6 +96,7 @@ /// Each arm's rule only matches if the arms before it have been seen, and it hasn't. /// So the arms must come in that order, once each. Anything else is a compile error. /// +/// [`DynCpu`]: super::DynCpu /// [`Generic`]: super::Generic macro_rules! cpu_select { // Entry point @@ -113,6 +127,8 @@ macro_rules! cpu_select { seen = [ avx2 ] arms = [ $($arms)* + #[cfg(not(target_arch = "x86_64"))] + crate::cpu::DynCpu::Avx2(_) => unreachable!(), #[cfg(target_arch = "x86_64")] crate::cpu::DynCpu::Avx2($($pat)*) => ] @@ -137,6 +153,8 @@ macro_rules! cpu_select { seen = [ avx2, neon ] arms = [ $($arms)* + #[cfg(not(target_arch = "aarch64"))] + crate::cpu::DynCpu::Neon(_) => unreachable!(), #[cfg(target_arch = "aarch64")] crate::cpu::DynCpu::Neon($($pat)*) => ] diff --git a/crates/oxc_lexer/src/cpu/tests.rs b/crates/oxc_lexer/src/cpu/tests.rs index 6f4c076713..96b20edb81 100644 --- a/crates/oxc_lexer/src/cpu/tests.rs +++ b/crates/oxc_lexer/src/cpu/tests.rs @@ -669,6 +669,12 @@ macro_rules! wrapped_tests { /// All these calls are safe. #[test] fn wrapped() { + // This module can see the CPU types' private fields. So on aarch64 with Neon, + // rustc knows that `Avx2` and `Generic` are uninhabited, and this always matches. + #[cfg_attr( + all(target_arch = "aarch64", target_feature = "neon"), + expect(irrefutable_let_patterns) + )] let DynCpu::$cpu(cpu) = DynCpu::detect() else { return }; assert_eq!(generics(&[1u8, 2], &[3, 4], false, cpu), 2); diff --git a/crates/oxc_lexer_macros/src/cpu.rs b/crates/oxc_lexer_macros/src/cpu.rs index 36c53b3c29..1aff311372 100644 --- a/crates/oxc_lexer_macros/src/cpu.rs +++ b/crates/oxc_lexer_macros/src/cpu.rs @@ -14,14 +14,17 @@ pub enum Cpu { Avx2, /// aarch64 with Neon Neon, - /// No SIMD. Exists on all platforms. + /// No SIMD Generic, } impl Cpu { + /// All the feature sets, in the order which `cpu_select!` requires its arms to be in. + pub const ALL: [Cpu; 3] = [Cpu::Avx2, Cpu::Neon, Cpu::Generic]; + /// The feature set called `ident`, e.g. the `Avx2` in `#[cpu_version(Avx2)]`. pub fn from_ident(ident: &Ident) -> Option { - [Cpu::Avx2, Cpu::Neon, Cpu::Generic].into_iter().find(|cpu| ident == cpu.name()) + Cpu::ALL.into_iter().find(|cpu| ident == cpu.name()) } /// Name of the type, and of the `DynCpu` variant. @@ -59,8 +62,8 @@ impl Cpu { quote!(crate::cpu::DynCpu::#name) } - /// `#[cfg(...)]` attribute for the platform the feature set exists on. - /// `None` for `Generic`, which exists everywhere. + /// `#[cfg(...)]` attribute for the platform which supports the feature set. + /// `None` for `Generic`, which every platform supports. pub fn cfg_attr(self) -> Option { let arch = match self { Cpu::Avx2 => "x86_64", diff --git a/crates/oxc_lexer_macros/src/lib.rs b/crates/oxc_lexer_macros/src/lib.rs index 8486ca91c1..ba9541f921 100644 --- a/crates/oxc_lexer_macros/src/lib.rs +++ b/crates/oxc_lexer_macros/src/lib.rs @@ -173,6 +173,8 @@ mod version; /// match to_dyn(cpu) { /// // SAFETY: `Avx2` type guarantees the CPU supports the features `__avx2` uses /// DynCpu::Avx2(cpu) => unsafe { __avx2(src, n, cpu) }, +/// // `Neon` type is uninhabited on x86_64 +/// DynCpu::Neon(_) => unreachable!(), /// // SAFETY: `__generic` only has `#[target_feature(enable = "sse2")]` /// // when SSE2 is enabled for the whole crate, so the CPU supports it /// DynCpu::Generic(cpu) => unsafe { __generic(src, n, cpu) }, @@ -186,6 +188,8 @@ mod version; /// } /// /// match to_dyn(cpu) { +/// // `Avx2` type is uninhabited on aarch64 +/// DynCpu::Avx2(_) => unreachable!(), /// // SAFETY: `Neon` type guarantees the CPU supports the features `__neon` uses /// DynCpu::Neon(cpu) => unsafe { __neon(src, n, cpu) }, /// DynCpu::Generic(cpu) => unsafe { __generic(src, n, cpu) }, @@ -193,6 +197,9 @@ mod version; /// } /// _ => { /// match to_dyn(cpu) { +/// // `Avx2` and `Neon` types are uninhabited on other platforms +/// DynCpu::Avx2(_) => unreachable!(), +/// DynCpu::Neon(_) => unreachable!(), /// // SAFETY: `__generic` only has `#[target_feature(enable = "sse2")]` /// // when SSE2 is enabled for the whole crate, so the CPU supports it /// DynCpu::Generic(cpu) => unsafe { __generic(src, n, cpu) }, @@ -202,6 +209,10 @@ mod version; /// } /// ``` /// +/// `DynCpu` has a variant for each feature set on every platform, so each `match` has an arm +/// for each. On a platform without a SIMD feature set, that feature set's type is uninhabited, +/// so its arm can never run, and is `unreachable!()`. +/// /// For a method, the `match` is the whole body of the outer method, in a `cfg_select!` of its /// own, and the other functions are methods next to it. #[proc_macro_attribute] diff --git a/crates/oxc_lexer_macros/src/multiversion.rs b/crates/oxc_lexer_macros/src/multiversion.rs index 24f9520700..edc2902081 100644 --- a/crates/oxc_lexer_macros/src/multiversion.rs +++ b/crates/oxc_lexer_macros/src/multiversion.rs @@ -83,6 +83,7 @@ impl Multiversion<'_> { /// /// match crate::cpu::to_dyn(cpu) { /// crate::cpu::DynCpu::Avx2(cpu) => unsafe { __avx2::(arr, cpu) }, + /// crate::cpu::DynCpu::Neon(_) => unreachable!(), /// crate::cpu::DynCpu::Generic(cpu) => unsafe { __generic::(arr, cpu) }, /// } /// } @@ -94,12 +95,15 @@ impl Multiversion<'_> { /// } /// /// match crate::cpu::to_dyn(cpu) { + /// crate::cpu::DynCpu::Avx2(_) => unreachable!(), /// crate::cpu::DynCpu::Neon(cpu) => unsafe { __neon::(arr, cpu) }, /// crate::cpu::DynCpu::Generic(cpu) => unsafe { __generic::(arr, cpu) }, /// } /// } /// _ => { /// match crate::cpu::to_dyn(cpu) { + /// crate::cpu::DynCpu::Avx2(_) => unreachable!(), + /// crate::cpu::DynCpu::Neon(_) => unreachable!(), /// crate::cpu::DynCpu::Generic(cpu) => unsafe { __generic::(arr, cpu) }, /// } /// } @@ -143,6 +147,7 @@ impl Multiversion<'_> { /// crate::cpu::DynCpu::Avx2(cpu) => unsafe { /// Self::__foo_avx2::(self, arr, cpu) /// }, + /// crate::cpu::DynCpu::Neon(_) => unreachable!(), /// crate::cpu::DynCpu::Generic(cpu) => unsafe { /// Self::__foo_generic::(self, arr, cpu) /// }, @@ -150,6 +155,7 @@ impl Multiversion<'_> { /// } /// target_arch = "aarch64" => { /// match crate::cpu::to_dyn(cpu) { + /// crate::cpu::DynCpu::Avx2(_) => unreachable!(), /// crate::cpu::DynCpu::Neon(cpu) => unsafe { /// Self::__foo_neon::(self, arr, cpu) /// }, @@ -160,6 +166,8 @@ impl Multiversion<'_> { /// } /// _ => { /// match crate::cpu::to_dyn(cpu) { + /// crate::cpu::DynCpu::Avx2(_) => unreachable!(), + /// crate::cpu::DynCpu::Neon(_) => unreachable!(), /// crate::cpu::DynCpu::Generic(cpu) => unsafe { /// Self::__foo_generic::(self, arr, cpu) /// }, @@ -296,18 +304,32 @@ impl Multiversion<'_> { } /// `match` which calls the entry point for the CPU's feature set. - /// `simd` is the SIMD feature set which exists on the platform, if there is one. + /// `simd` is the SIMD feature set which the platform has, if there is one. + /// + /// `DynCpu` has a variant for each feature set on every platform. + /// The type of a SIMD feature set which the platform doesn't have is uninhabited, + /// so its arm can never run, and is `unreachable!()`. + /// The arm is still needed, because the type's field is private, so outside the module which + /// defines the type, rustc doesn't know that it's uninhabited. /// /// ```ignore /// match crate::cpu::to_dyn(cpu) { /// crate::cpu::DynCpu::Avx2(cpu) => unsafe { __avx2::(arr, cpu) }, + /// crate::cpu::DynCpu::Neon(_) => unreachable!(), /// crate::cpu::DynCpu::Generic(cpu) => unsafe { __generic::(arr, cpu) }, /// } /// ``` fn cpu_match(&self, simd: Option) -> TokenStream { let cpu_name = self.cpu_param.arg(); - let arms = simd.into_iter().chain([Cpu::Generic]).map(|cpu| { + let arms = Cpu::ALL.into_iter().map(|cpu| { let variant = cpu.variant(); + + if cpu != Cpu::Generic && simd != Some(cpu) { + return quote! { + #variant(_) => unreachable!(), + }; + } + let call = self.call(cpu.lower_name()); // SAFETY: `Avx2` and `Neon` types guarantee the CPU supports the features their