diff --git a/crates/oxc_lexer/src/cpu/mod.rs b/crates/oxc_lexer/src/cpu/mod.rs index 50b8dc44cb..c37f0ac684 100644 --- a/crates/oxc_lexer/src/cpu/mod.rs +++ b/crates/oxc_lexer/src/cpu/mod.rs @@ -13,7 +13,25 @@ //! It's the best one which is known at compile time to be available. //! //! Code which has SIMD implementations takes a type implementing [`Cpu`], -//! and uses [`cpu_select!`] to pick an implementation. +//! and picks an implementation with a `match` on [`to_dyn`]: +//! +//! ```ignore +//! match to_dyn(cpu) { +//! DynCpu::Avx2(cpu) => classify_avx2(self, cpu), +//! DynCpu::Neon(cpu) => classify_neon(self, cpu), +//! DynCpu::Generic(_) => classify_generic(self), +//! } +//! ``` +//! +//! [`DynCpu`] has all 3 variants on every platform. On a platform which doesn't have +//! a feature set, its type is uninhabited, so its arm can never run, and the compiler removes it. +//! The arm is still needed, because the type's field is private, so outside this module +//! rustc doesn't know that it's uninhabited. +//! +//! Use the [`to_dyn`] function, not the [`Cpu::to_dyn`] method. The method is an extra +//! `#[inline(always)]` function around it, and that changes the code which some callers +//! compile to, because rustc's MIR inliner treats them differently. +//! //! [`#[cpu_version]`] marks each implementation with the CPU feature set it's for. //! [`#[cpu_multiversion]`] turns a function into one with a separate entry point for each CPU feature set. //! [`#[cpu_impl]`] marks methods which only work on one CPU feature set's platform. @@ -23,7 +41,7 @@ //! * Given a specific type (e.g. `Avx2`): the choice is made at compile time. //! * Given a [`DynCpu`]: the choice is made at runtime. //! -//! Functions which use [`cpu_select!`] typically take a generic `impl Cpu`. +//! Functions which pick an implementation typically take a generic `impl Cpu`. //! This compiles a separate version of the function for each [`Cpu`] type that callers use with it. //! //! [`DynCpu`] also implements [`Cpu`], so functions that receive an `impl Cpu` @@ -66,13 +84,9 @@ use std::{convert::Infallible as Never, mem}; pub(crate) use oxc_lexer_macros::{cpu_impl, cpu_multiversion, cpu_version}; -mod select_macro; - #[cfg(test)] mod tests; -pub(crate) use select_macro::cpu_select; - /// Trait for types representing a CPU feature set. /// /// Implemented by all types representing specific CPU feature sets, and by [`DynCpu`]. @@ -143,7 +157,9 @@ impl CpuPrivate for DynCpu { /// /// Same as [`Cpu::to_dyn`], but this works in `const` code too. /// A `const fn` can't call a trait method on a generic type, so it can't call [`Cpu::to_dyn`]. -/// [`cpu_select!`] and [`#[cpu_multiversion]`] use this function, so they work in `const` code. +/// [`#[cpu_multiversion]`] uses this function, so it works in `const` code. +/// Code which picks an implementation uses it too: `match to_dyn(cpu) { ... }`. +/// See the module docs for why it's preferred to [`Cpu::to_dyn`] there. /// /// Only [`DynCpu`] needs `cpu`. It's taken for all types so that this can only be called /// by code which already has one of these types. diff --git a/crates/oxc_lexer/src/cpu/select_macro.rs b/crates/oxc_lexer/src/cpu/select_macro.rs deleted file mode 100644 index de87dd47fe..0000000000 --- a/crates/oxc_lexer/src/cpu/select_macro.rs +++ /dev/null @@ -1,248 +0,0 @@ -/// Macro for either static or dynamic feature selection. -/// -/// Arms use normal `match` syntax, but there must be exactly these 3 arms, in this order: -/// -/// - `Avx2(...)` -/// - `Neon(...)` -/// - `Generic(...)` -/// -/// All 3 arms are compiled and type-checked on every platform. -/// [`DynCpu`] has all 3 variants on every platform. On a platform which doesn't have -/// a feature set, its type is uninhabited, so its arm can never run, and the compiler removes it. -/// e.g. `Avx2` on aarch64, or [`Generic`] on aarch64 with Neon enabled at compile time. -/// 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. -/// -/// Requiring all 3 arms means every call site says what to do on every platform. -/// It also makes the `match` exhaustive on every platform, without a fallback arm. -/// -/// # Examples -/// -/// ## Static selection -/// -/// ```ignore -/// use crate::cpu::{Cpu, cpu_select}; -/// -/// fn classify(&mut self, cpu: impl Cpu) { -/// // Compiles down to a single arm (no runtime branch) -/// cpu_select!(cpu, { -/// Avx2(cpu) => classify_avx2(self, cpu), -/// Neon(cpu) => classify_neon(self, cpu), -/// Generic(_) => classify_generic(self), -/// }); -/// } -/// ``` -/// -/// ## Dynamic selection -/// -/// ```ignore -/// use crate::cpu::{DynCpu, cpu_select}; -/// -/// fn classify(cpu: DynCpu) { -/// // Runtime match over options available on the current platform -/// cpu_select!(cpu, { -/// Avx2(cpu) => classify_avx2(self, cpu), -/// Neon(cpu) => classify_neon(self, cpu), -/// Generic(_) => classify_generic(self), -/// }); -/// } -/// ``` -/// -/// ## Expansion -/// -/// Macro expands to: -/// -/// ```ignore -/// match to_dyn(cpu) { -/// DynCpu::Avx2(cpu) => classify_avx2(self, cpu), -/// DynCpu::Neon(cpu) => classify_neon(self, cpu), -/// DynCpu::Generic(_) => classify_generic(self), -/// } -/// ``` -/// -/// ## Implementation -/// -/// Tokens of the arms are consumed one at a time, and copied into `arms`. -/// `Avx2(...) =>` is rewritten to `DynCpu::Avx2(...) =>`, and the same for `Neon` and `Generic`. -/// All other tokens are copied unchanged, so the arm bodies are parsed by the `match` itself. -/// -/// State is a set of named slots: -/// -/// * `rest` is the tokens which haven't been consumed yet. -/// * `seen` lists the arms which have been seen so far (e.g. `[ avx2, neon ]`). -/// * `arms` is the tokens of the rewritten arms so far. -/// * `cpu` is the expression being matched on. -/// -/// The slots which tell the rules apart (`rest`, then `seen`) come first, and `cpu`, which is only -/// passed on, comes last. So when a rule doesn't match, the compiler finds out early. -/// -/// 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 - ($cpu:expr, { $($arms:tt)* }) => { - crate::cpu::cpu_select!( - @munch - rest = [ $($arms)* ] - seen = [] - arms = [] - cpu = $cpu - ) - }; - - // `Avx2` arm. Must be first. - ( - @munch - rest = [ - Avx2($($pat:tt)*) => - $($rest:tt)* - ] - seen = [] - arms = [ $($arms:tt)* ] - cpu = $cpu:tt - ) => { - crate::cpu::cpu_select!( - @munch - rest = [ $($rest)* ] - seen = [ avx2 ] - arms = [ - $($arms)* - crate::cpu::DynCpu::Avx2($($pat)*) => - ] - cpu = $cpu - ) - }; - - // `Neon` arm. Must follow `Avx2` arm. - ( - @munch - rest = [ - Neon($($pat:tt)*) => - $($rest:tt)* - ] - seen = [ avx2 ] - arms = [ $($arms:tt)* ] - cpu = $cpu:tt - ) => { - crate::cpu::cpu_select!( - @munch - rest = [ $($rest)* ] - seen = [ avx2, neon ] - arms = [ - $($arms)* - crate::cpu::DynCpu::Neon($($pat)*) => - ] - cpu = $cpu - ) - }; - - // `Generic` arm. Must follow `Neon` arm. - ( - @munch - rest = [ - Generic($($pat:tt)*) => - $($rest:tt)* - ] - seen = [ avx2, neon ] - arms = [ $($arms:tt)* ] - cpu = $cpu:tt - ) => { - crate::cpu::cpu_select!( - @munch - rest = [ $($rest)* ] - seen = [ avx2, neon, generic ] - arms = [ - $($arms)* - crate::cpu::DynCpu::Generic($($pat)*) => - ] - cpu = $cpu - ) - }; - - // Any other `=>` - an arm which is out of order, repeated, not one of the 3 names, - // a fallback (e.g. `_ =>`), or has a match guard - ( - @munch - rest = [ - => - $($_rest:tt)* - ] - $($_any:tt)* - ) => { - crate::cpu::cpu_select!(@error) - }; - - // Any other token. - // - // Copy 1 token, and go round again. - // - // The arm rules only consume `Avx2(...) =>` etc, not the arm's body. - // So the remaining tokens can't all be copied in one go. They start with the body, - // and a `Neon(...) =>` which still needs rewriting may come after it. - // - // `macro_rules!` can't match "all tokens up to the next `Neon`", - // because `$($x:tt)*` would also match `Neon`. - // The only way to find the next arm is to step through the tokens. - // - // Consuming the body in the arm rules would avoid this, but would need 2 rules for each arm - // (block body, or expression followed by `,`), and would reject comma-free bodies like `unsafe { ... }`. - // - // A delimited group (e.g. a block body) is a single token, so its contents are never inspected. - // This rule must come after the arm rules and the error rule above, - // because `$next:tt` also matches `Avx2` and `=>`. - ( - @munch - rest = [ - $next:tt - $($rest:tt)* - ] - seen = $seen:tt - arms = [ $($arms:tt)* ] - cpu = $cpu:tt - ) => { - crate::cpu::cpu_select!( - @munch - rest = [ $($rest)* ] - seen = $seen - arms = [ - $($arms)* - $next - ] - cpu = $cpu - ) - }; - - // All tokens consumed, and all 3 arms seen - ( - @munch - rest = [] - seen = [ avx2, neon, generic ] - arms = [ $($arms:tt)* ] - cpu = $cpu:tt - ) => { - match crate::cpu::to_dyn($cpu) { - $($arms)* - } - }; - - // All tokens consumed, but an arm is missing. - // The rules above handle every other case, so this rule matches anything. - (@munch $($_any:tt)*) => { - crate::cpu::cpu_select!(@error) - }; - - // Error for any arms which don't follow the rules - (@error) => { - compile_error!( - "`cpu_select!` must have arms `Avx2(...)`, `Neon(...)` and `Generic(...)`, in that order, once each" - ) - }; -} - -pub(crate) use cpu_select; diff --git a/crates/oxc_lexer/src/cpu/tests.rs b/crates/oxc_lexer/src/cpu/tests.rs index 5d89a588f0..f9afbcff81 100644 --- a/crates/oxc_lexer/src/cpu/tests.rs +++ b/crates/oxc_lexer/src/cpu/tests.rs @@ -1,6 +1,6 @@ use std::panic::Location; -use super::{Avx2, BASELINE, Cpu, DynCpu, Neon, cpu_multiversion, cpu_select, cpu_version, to_dyn}; +use super::{Avx2, BASELINE, Cpu, DynCpu, Neon, cpu_multiversion, cpu_version, to_dyn}; use crate::simd::{Vec128, Vec256}; #[cpu_multiversion] @@ -323,11 +323,11 @@ fn const_fn_at_runtime() { assert_eq!(Counter::of(5, cpu).0, 5); assert_eq!(Counter::double_of(6, cpu), 12); - cpu_select!(cpu, { - Avx2(cpu) => assert_eq!(add_one(1, cpu), 2), - Neon(cpu) => assert_eq!(add_one(1, cpu), 2), - Generic(cpu) => assert_eq!(add_one(1, cpu), 2), - }); + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => assert_eq!(add_one(1, cpu), 2), + DynCpu::Neon(cpu) => assert_eq!(add_one(1, cpu), 2), + DynCpu::Generic(cpu) => assert_eq!(add_one(1, cpu), 2), + } } #[test] @@ -394,11 +394,11 @@ fn double_generic(x: u32) -> u32 { /// `double_avx2` and `double_neon` have a parameter which proves the CPU supports the feature set, /// so calling them is safe. fn double(x: u32, cpu: impl Cpu) -> u32 { - cpu_select!(cpu, { - Avx2(cpu) => double_avx2(x, cpu), - Neon(cpu) => double_neon(x, cpu), - Generic(_) => double_generic(x), - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => double_avx2(x, cpu), + DynCpu::Neon(cpu) => double_neon(x, cpu), + DynCpu::Generic(_) => double_generic(x), + } } #[cpu_version(Avx2)] @@ -418,11 +418,11 @@ fn triple_neon(x: u32, _cpu: Neon) -> u32 { /// `triple_avx2` and `triple_neon` are `#[inline(always)]`, so have no `#[target_feature]`, /// and calling them is safe. fn triple(x: u32, cpu: impl Cpu) -> u32 { - cpu_select!(cpu, { - Avx2(cpu) => triple_avx2(x, cpu), - Neon(cpu) => triple_neon(x, cpu), - Generic(_) => x * 3, - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => triple_avx2(x, cpu), + DynCpu::Neon(cpu) => triple_neon(x, cpu), + DynCpu::Generic(_) => x * 3, + } } /// A `cpu` parameter doesn't have to be the last parameter. @@ -439,11 +439,11 @@ fn quadruple_neon(_cpu: Neon, x: u32) -> u32 { } fn quadruple(x: u32, cpu: impl Cpu) -> u32 { - cpu_select!(cpu, { - Avx2(cpu) => quadruple_avx2(cpu, x), - Neon(cpu) => quadruple_neon(cpu, x), - Generic(_) => x * 4, - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => quadruple_avx2(cpu, x), + DynCpu::Neon(cpu) => quadruple_neon(cpu, x), + DynCpu::Generic(_) => x * 4, + } } /// A vector parameter proves the CPU supports the feature set, in place of a `cpu` parameter. @@ -460,11 +460,11 @@ fn is_zero_neon(v: Vec128) -> bool { } fn is_zero(cpu: impl Cpu) -> bool { - cpu_select!(cpu, { - Avx2(cpu) => is_zero_avx2(Vec128::splat(0, cpu)), - Neon(cpu) => is_zero_neon(Vec128::splat(0, cpu)), - Generic(_) => true, - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => is_zero_avx2(Vec128::splat(0, cpu)), + DynCpu::Neon(cpu) => is_zero_neon(Vec128::splat(0, cpu)), + DynCpu::Generic(_) => true, + } } /// A vector parameter doesn't have to be the last parameter. @@ -483,11 +483,11 @@ fn add_if_zero_neon(x: u32, v: Vec256, y: u32) -> u32 { } fn add_if_zero(x: u32, y: u32, cpu: impl Cpu) -> u32 { - cpu_select!(cpu, { - Avx2(cpu) => add_if_zero_avx2(x, Vec256::splat(0, cpu), y), - Neon(cpu) => add_if_zero_neon(x, Vec256::splat(0, cpu), y), - Generic(_) => x + y, - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => add_if_zero_avx2(x, Vec256::splat(0, cpu), y), + DynCpu::Neon(cpu) => add_if_zero_neon(x, Vec256::splat(0, cpu), y), + DynCpu::Generic(_) => x + y, + } } /// An `unsafe` function isn't wrapped, so it has `#[target_feature]`, as well as being unsafe. @@ -504,13 +504,13 @@ unsafe fn halve_neon(x: u32, _cpu: Neon) -> u32 { } fn halve(x: u32, cpu: impl Cpu) -> u32 { - cpu_select!(cpu, { + match to_dyn(cpu) { // SAFETY: `Avx2` type guarantees the CPU supports the features `halve_avx2` uses - Avx2(cpu) => unsafe { halve_avx2(x, cpu) }, + DynCpu::Avx2(cpu) => unsafe { halve_avx2(x, cpu) }, // SAFETY: `Neon` type guarantees the CPU supports the features `halve_neon` uses - Neon(cpu) => unsafe { halve_neon(x, cpu) }, - Generic(_) => x / 2, - }) + DynCpu::Neon(cpu) => unsafe { halve_neon(x, cpu) }, + DynCpu::Generic(_) => x / 2, + } } /// Tests of functions which `cpu_version!` wraps in a safe function, for one CPU feature set. diff --git a/crates/oxc_lexer/src/pipeline/bitmap.rs b/crates/oxc_lexer/src/pipeline/bitmap.rs index 5a48724be2..cab2eff531 100644 --- a/crates/oxc_lexer/src/pipeline/bitmap.rs +++ b/crates/oxc_lexer/src/pipeline/bitmap.rs @@ -1,5 +1,5 @@ use crate::{ - cpu::{Avx2, Cpu, cpu_multiversion, cpu_select, cpu_version}, + cpu::{Avx2, Cpu, DynCpu, cpu_multiversion, cpu_version, to_dyn}, simd::Vec256, }; @@ -149,11 +149,10 @@ pub(super) unsafe fn bm_clear_range(bm: *mut u64, a: usize, b: usize, _cpu: impl #[cpu_multiversion] #[inline(always)] pub(super) unsafe fn bm_any(bm: *const u64, nw: usize, cpu: impl Cpu) -> bool { - cpu_select!(cpu, { - Avx2(cpu) => bm_any_avx2(bm, nw, cpu), - Neon(_) => bm_any_generic(bm, nw), - Generic(_) => bm_any_generic(bm, nw), - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => bm_any_avx2(bm, nw, cpu), + _ => bm_any_generic(bm, nw), + } } /// [`bm_any`] for AVX2. diff --git a/crates/oxc_lexer/src/pipeline/carve/jsx/hyphens.rs b/crates/oxc_lexer/src/pipeline/carve/jsx/hyphens.rs index cbb735a555..ad8545e133 100644 --- a/crates/oxc_lexer/src/pipeline/carve/jsx/hyphens.rs +++ b/crates/oxc_lexer/src/pipeline/carve/jsx/hyphens.rs @@ -1,5 +1,5 @@ use crate::{ - cpu::{Avx2, BASELINE, Cpu, cpu_multiversion, cpu_select, cpu_version}, + cpu::{Avx2, BASELINE, Cpu, DynCpu, cpu_multiversion, cpu_version, to_dyn}, simd::Vec256, }; @@ -25,11 +25,10 @@ pub(super) unsafe fn jsx_glue_hyphens( b: usize, cpu: impl Cpu, ) { - cpu_select!(cpu, { - Avx2(cpu) => jsx_glue_hyphens_avx2(src, n, st, opch, word, a, b, cpu), - Neon(_) => jsx_glue_hyphens_generic(src, n, st, opch, word, a, b), - Generic(_) => jsx_glue_hyphens_generic(src, n, st, opch, word, a, b), - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => jsx_glue_hyphens_avx2(src, n, st, opch, word, a, b, cpu), + _ => jsx_glue_hyphens_generic(src, n, st, opch, word, a, b), + } } /// [`jsx_glue_hyphens`] for AVX2. diff --git a/crates/oxc_lexer/src/pipeline/classify/mod.rs b/crates/oxc_lexer/src/pipeline/classify/mod.rs index d7bcefd312..32e2fdb8ab 100644 --- a/crates/oxc_lexer/src/pipeline/classify/mod.rs +++ b/crates/oxc_lexer/src/pipeline/classify/mod.rs @@ -1,4 +1,4 @@ -use crate::cpu::{Cpu, cpu_multiversion, cpu_select}; +use crate::cpu::{Cpu, DynCpu, cpu_multiversion, to_dyn}; mod avx2; use avx2::classify_avx2; @@ -31,11 +31,17 @@ pub(super) unsafe fn classify( kind: *mut u8, cpu: impl Cpu, ) { - cpu_select!(cpu, { - Avx2(cpu) => classify_avx2(ts, src, n, word, st, kwinit, opch, digit, dot, misc, kind, cpu), - Neon(cpu) => classify_neon(ts, src, n, word, st, kwinit, opch, digit, dot, misc, kind, cpu), - Generic(_) => classify_generic(ts, src, n, word, st, kwinit, opch, digit, dot, misc, kind), - }); + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => { + classify_avx2(ts, src, n, word, st, kwinit, opch, digit, dot, misc, kind, cpu) + } + DynCpu::Neon(cpu) => { + classify_neon(ts, src, n, word, st, kwinit, opch, digit, dot, misc, kind, cpu) + } + DynCpu::Generic(_) => { + classify_generic(ts, src, n, word, st, kwinit, opch, digit, dot, misc, kind) + } + }; *word.add(nb) = 0; *st.add(nb) = 0; diff --git a/crates/oxc_lexer/src/pipeline/compress/mod.rs b/crates/oxc_lexer/src/pipeline/compress/mod.rs index 458fa995d2..fe87748d52 100644 --- a/crates/oxc_lexer/src/pipeline/compress/mod.rs +++ b/crates/oxc_lexer/src/pipeline/compress/mod.rs @@ -1,7 +1,7 @@ use oxc_span::Span; use crate::{ - cpu::{Cpu, DynCpu, cpu_multiversion, cpu_select}, + cpu::{Cpu, DynCpu, cpu_multiversion, to_dyn}, token::{SPAN_SENTINELS, tk}, }; @@ -71,11 +71,9 @@ pub unsafe fn write_sentinels(n: u32, spans: *mut Span, sig_kinds: *mut u8, _cpu /// /// `PairLuts` is only used by AVX2 implementation, so this function is a no-op on other CPUs. pub(super) fn init_pair_luts(cpu: DynCpu) { - cpu_select!(cpu, { - Avx2(cpu) => init_pair_luts_avx2(cpu), - Neon(_) => {} - Generic(_) => {} - }); + if let DynCpu::Avx2(cpu) = cpu { + init_pair_luts_avx2(cpu); + } } #[cpu_multiversion] @@ -89,11 +87,10 @@ unsafe fn compress_blocks( kinds: *mut u8, cpu: impl Cpu, ) -> usize { - cpu_select!(cpu, { - Avx2(cpu) => compress_blocks_avx2(st, kind, b0, b1, starts, kinds, cpu), - Neon(_) => compress_blocks_generic(st, kind, b0, b1, starts, kinds), - Generic(_) => compress_blocks_generic(st, kind, b0, b1, starts, kinds), - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => compress_blocks_avx2(st, kind, b0, b1, starts, kinds, cpu), + _ => compress_blocks_generic(st, kind, b0, b1, starts, kinds), + } } #[cpu_multiversion] @@ -106,11 +103,10 @@ unsafe fn build_spans( sig_kinds: *mut u8, cpu: impl Cpu, ) -> usize { - cpu_select!(cpu, { - Avx2(cpu) => build_spans_avx2(stage_kind, stage_pos, m, spans, sig_kinds, cpu), - Neon(_) => build_spans_generic(stage_kind, stage_pos, m, spans, sig_kinds), - Generic(_) => build_spans_generic(stage_kind, stage_pos, m, spans, sig_kinds), - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => build_spans_avx2(stage_kind, stage_pos, m, spans, sig_kinds, cpu), + _ => build_spans_generic(stage_kind, stage_pos, m, spans, sig_kinds), + } } #[cpu_multiversion] @@ -124,9 +120,8 @@ unsafe fn lanes_post( lanes: &mut Lanes, cpu: impl Cpu, ) { - cpu_select!(cpu, { - Avx2(cpu) => lanes_post_avx2(src, out_kinds, out_spans, m, nn, lanes, cpu), - Neon(_) => lanes_post_generic(src, out_kinds, out_spans, m, nn, lanes), - Generic(_) => lanes_post_generic(src, out_kinds, out_spans, m, nn, lanes), - }); + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => lanes_post_avx2(src, out_kinds, out_spans, m, nn, lanes, cpu), + _ => lanes_post_generic(src, out_kinds, out_spans, m, nn, lanes), + }; } diff --git a/crates/oxc_lexer/src/pipeline/find/mod.rs b/crates/oxc_lexer/src/pipeline/find/mod.rs index b53ba8e99b..307858d9e0 100644 --- a/crates/oxc_lexer/src/pipeline/find/mod.rs +++ b/crates/oxc_lexer/src/pipeline/find/mod.rs @@ -1,4 +1,4 @@ -use crate::cpu::{Cpu, cpu_multiversion, cpu_select}; +use crate::cpu::{Cpu, DynCpu, cpu_multiversion, to_dyn}; mod avx2; use avx2::{bracket_bits_avx2, find_avx2}; @@ -18,20 +18,18 @@ pub(super) unsafe fn find( needles: [u8; K], cpu: impl Cpu, ) -> usize { - cpu_select!(cpu, { - Avx2(cpu) => find_avx2(src, n, i, needles, cpu), - Neon(_) => find_generic(src, n, i, needles), - Generic(_) => find_generic(src, n, i, needles), - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => find_avx2(src, n, i, needles, cpu), + _ => find_generic(src, n, i, needles), + } } /// Bits of the 64 bytes at `base` that are brackets (`(){}[]`). Bit `i` is for byte `base + i`. #[cpu_multiversion] #[inline(always)] pub(super) fn bracket_bits(src: &[u8], base: usize, cpu: impl Cpu) -> u64 { - cpu_select!(cpu, { - Avx2(cpu) => bracket_bits_avx2(src, base, cpu), - Neon(_) => bracket_bits_generic(src, base), - Generic(_) => bracket_bits_generic(src, base), - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => bracket_bits_avx2(src, base, cpu), + _ => bracket_bits_generic(src, base), + } } diff --git a/crates/oxc_lexer/src/pipeline/keywords.rs b/crates/oxc_lexer/src/pipeline/keywords.rs index 2e7a6fdb54..a3120ede5a 100644 --- a/crates/oxc_lexer/src/pipeline/keywords.rs +++ b/crates/oxc_lexer/src/pipeline/keywords.rs @@ -37,7 +37,7 @@ use constcat::concat_slices; use oxc_data_structures::{assert_unchecked, branch_hints::unlikely, str::const_str_eq}; use crate::{ - cpu::{Avx2, BASELINE, Cpu, Neon, cpu_multiversion, cpu_select, cpu_version}, + cpu::{Avx2, BASELINE, Cpu, DynCpu, Neon, cpu_multiversion, cpu_version, to_dyn}, simd::Vec128, token::{KW_KIND_BASE, TokenKind}, }; @@ -507,11 +507,11 @@ pub(super) unsafe fn kw_match( #[cpu_multiversion] #[inline(always)] unsafe fn text_matches(ptr: *const u8, len: usize, text: &[u64; 2], cpu: impl Cpu) -> bool { - cpu_select!(cpu, { - Avx2(cpu) => text_matches_avx2(ptr, len, text, cpu), - Neon(cpu) => text_matches_neon(ptr, len, text, cpu), - Generic(_) => text_matches_generic(ptr, len, text), - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => text_matches_avx2(ptr, len, text, cpu), + DynCpu::Neon(cpu) => text_matches_neon(ptr, len, text, cpu), + DynCpu::Generic(_) => text_matches_generic(ptr, len, text), + } } /// [`text_matches`] for AVX2. diff --git a/crates/oxc_lexer/src/pipeline/lanes.rs b/crates/oxc_lexer/src/pipeline/lanes.rs index d450b469de..ef9446d780 100644 --- a/crates/oxc_lexer/src/pipeline/lanes.rs +++ b/crates/oxc_lexer/src/pipeline/lanes.rs @@ -9,7 +9,7 @@ use oxc_ast::ast::{Comment, RegExpFlags}; use oxc_syntax::identifier::{is_identifier_part, is_identifier_start}; use crate::{ - cpu::{Avx2, BASELINE, Cpu, cpu_multiversion, cpu_select, cpu_version}, + cpu::{Avx2, BASELINE, Cpu, DynCpu, cpu_multiversion, cpu_version, to_dyn}, error::{DiagCode, DiagSeverity, Diagnostic}, simd::Vec256, token::StringSpan, @@ -481,13 +481,12 @@ fn cook_short( diags: &mut Vec, cpu: impl Cpu, ) -> (u32, bool, bool) { - cpu_select!(cpu, { - Avx2(cpu) => { + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => { cook_short_avx2::(src, bs, be, base, ss, body_len, diags, cpu) } - Neon(_) => cook_short_generic::(src, bs, be, base, ss, body_len, diags), - Generic(_) => cook_short_generic::(src, bs, be, base, ss, body_len, diags), - }) + _ => cook_short_generic::(src, bs, be, base, ss, body_len, diags), + } } /// [`cook_short`] for AVX2. @@ -555,11 +554,10 @@ fn cook_short_generic( #[cpu_multiversion] #[inline(always)] fn span_has_bs(src: &[u8], bs: usize, be: usize, cpu: impl Cpu) -> bool { - cpu_select!(cpu, { - Avx2(cpu) => span_has_bs_avx2(src, bs, be, cpu), - Neon(_) => span_has_bs_generic(src, bs, be), - Generic(_) => span_has_bs_generic(src, bs, be), - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => span_has_bs_avx2(src, bs, be, cpu), + _ => span_has_bs_generic(src, bs, be), + } } /// [`span_has_bs`] for AVX2. @@ -595,11 +593,10 @@ fn span_has_bs_generic(src: &[u8], bs: usize, be: usize) -> bool { #[cpu_multiversion] #[inline(always)] fn span_has_bs_or_cr(src: &[u8], bs: usize, be: usize, cpu: impl Cpu) -> bool { - cpu_select!(cpu, { - Avx2(cpu) => span_has_bs_or_cr_avx2(src, bs, be, cpu), - Neon(_) => span_has_bs_or_cr_generic(src, bs, be), - Generic(_) => span_has_bs_or_cr_generic(src, bs, be), - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => span_has_bs_or_cr_avx2(src, bs, be, cpu), + _ => span_has_bs_or_cr_generic(src, bs, be), + } } /// [`span_has_bs_or_cr`] for AVX2. diff --git a/crates/oxc_lexer/src/pipeline/scan/mod.rs b/crates/oxc_lexer/src/pipeline/scan/mod.rs index 729349c132..4211e8781f 100644 --- a/crates/oxc_lexer/src/pipeline/scan/mod.rs +++ b/crates/oxc_lexer/src/pipeline/scan/mod.rs @@ -1,4 +1,4 @@ -use crate::cpu::{Cpu, cpu_multiversion, cpu_select}; +use crate::cpu::{Cpu, DynCpu, cpu_multiversion, to_dyn}; mod avx2; use avx2::{scan_block_comment_avx2, scan_line_comment_avx2}; @@ -17,11 +17,10 @@ pub(super) unsafe fn scan_block_comment( i: usize, cpu: impl Cpu, ) -> (usize, bool, i64) { - cpu_select!(cpu, { - Avx2(cpu) => scan_block_comment_avx2(src, n, i, cpu), - Neon(_) => scan_block_comment_generic(src, n, i), - Generic(_) => scan_block_comment_generic(src, n, i), - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => scan_block_comment_avx2(src, n, i, cpu), + _ => scan_block_comment_generic(src, n, i), + } } /// Scan a `//` line comment to its LineTerminator (LF, CR, or LS/PS), @@ -34,9 +33,8 @@ pub(super) unsafe fn scan_line_comment( i: usize, cpu: impl Cpu, ) -> (usize, i64) { - cpu_select!(cpu, { - Avx2(cpu) => scan_line_comment_avx2(src, n, i, cpu), - Neon(_) => scan_line_comment_generic(src, n, i), - Generic(_) => scan_line_comment_generic(src, n, i), - }) + match to_dyn(cpu) { + DynCpu::Avx2(cpu) => scan_line_comment_avx2(src, n, i, cpu), + _ => scan_line_comment_generic(src, n, i), + } } diff --git a/crates/oxc_lexer/src/simd/prefetch.rs b/crates/oxc_lexer/src/simd/prefetch.rs index b5ccc7e8a1..fc2de85243 100644 --- a/crates/oxc_lexer/src/simd/prefetch.rs +++ b/crates/oxc_lexer/src/simd/prefetch.rs @@ -3,7 +3,7 @@ #[cfg(target_arch = "x86_64")] use std::arch::x86_64::{_MM_HINT_T0, _mm_prefetch}; -use crate::cpu::{Cpu, cpu_multiversion, cpu_select}; +use crate::cpu::{Cpu, DynCpu, cpu_multiversion, to_dyn}; /// Ask the CPU to fetch the cache line containing `p` into all levels of the cache. /// @@ -14,17 +14,11 @@ use crate::cpu::{Cpu, cpu_multiversion, cpu_select}; pub fn prefetch(p: *const u8, cpu: impl Cpu) { let _ = p; // Avoid clippy unused vars warning - cpu_select!(cpu, { - Avx2(_) => { - cfg_select! { - target_arch = "x86_64" => { - // SAFETY: `Avx2` type guarantees CPU supports AVX2 - unsafe { _mm_prefetch(p.cast::(), _MM_HINT_T0) } - } - _ => {} - } + if matches!(to_dyn(cpu), DynCpu::Avx2(_)) { + // SAFETY: `Avx2` type guarantees CPU supports AVX2 + #[cfg(target_arch = "x86_64")] + unsafe { + _mm_prefetch(p.cast::(), _MM_HINT_T0); } - Neon(_) => { /* no-op */ } - Generic(_) => { /* no-op */ } - }); + } } diff --git a/crates/oxc_lexer_macros/src/lib.rs b/crates/oxc_lexer_macros/src/lib.rs index eaece20296..e045055991 100644 --- a/crates/oxc_lexer_macros/src/lib.rs +++ b/crates/oxc_lexer_macros/src/lib.rs @@ -11,7 +11,7 @@ //! - [`cpu_impl`]: Methods of one feature set's types, which exist on every platform, //! but only work on the platform which has the feature set. //! -//! `oxc_lexer`'s `cpu_select!` macro chooses between versions. +//! `oxc_lexer` code chooses between versions with a `match` on `to_dyn(cpu)`. //! //! The macros are only for `oxc_lexer`. //! Their output refers to its `cpu` and `simd` modules by path. @@ -228,7 +228,7 @@ pub fn cpu_multiversion(args: TokenStream, item: TokenStream) -> TokenStream { /// Marks a function as the version of some code for one CPU feature set. /// -/// `cpu_select!` picks between the versions. +/// A `match` on `to_dyn(cpu)` picks between the versions. /// /// Applied to a function with one of: ///