mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
refactor(lexer): pick span_has_bs versions with cpu_select! (#28639)
Part of effort to move to runtime CPU feature detection. Replace the cfg'd versions of `span_has_bs` and `span_has_bs_or_cr` in `lanes.rs` with `#[inline(always)]` dispatchers which take `cpu` and use `cpu_select!`. The AVX2 versions get `#[target_feature]` and take `_cpu: Avx2`. They no longer need `unsafe` blocks around the intrinsics which don't access memory. Their only caller `Lanes::cook` passes `cpu`. Codegen: aarch64 is unchanged. With AVX2 flags, the AVX2 copies are unchanged, and the generic copies of `push_string`, `push_template` and `push_atom` now use the `memchr` versions, as intended. In the x86_64 build, their AVX2 copies now inline the AVX2 versions instead of calling `memchr`. They now have the same instructions as in the build with AVX2 flags. The generic code's instruction count is unchanged.
This commit is contained in:
1 parent
6c05533200
commit
884a6e6337
1 file changed
+43
-38
@@ -180,9 +180,9 @@ impl Lanes {
|
||||
let needs_decode = if body_len < 32 {
|
||||
return self.cook_short_dispatch::<EMIT, CRLF>(src, bs, be, base, ss, body_len, cpu);
|
||||
} else if CRLF {
|
||||
span_has_bs_or_cr(src, bs as usize, be as usize)
|
||||
span_has_bs_or_cr(src, bs as usize, be as usize, cpu)
|
||||
} else {
|
||||
span_has_bs(src, bs as usize, be as usize)
|
||||
span_has_bs(src, bs as usize, be as usize, cpu)
|
||||
};
|
||||
let (wi, lone, nes) = if needs_decode {
|
||||
unsafe {
|
||||
@@ -531,19 +531,27 @@ fn cook_short_generic<const EMIT: bool, const CRLF: bool>(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
target_feature = "avx2",
|
||||
target_feature = "bmi2",
|
||||
target_feature = "popcnt"
|
||||
))]
|
||||
#[inline(always)]
|
||||
fn span_has_bs(src: &[u8], bs: usize, be: usize, cpu: impl Cpu) -> bool {
|
||||
cpu_select!(cpu, {
|
||||
Avx2(cpu) => {
|
||||
// SAFETY: `Avx2` type guarantees required target features are present
|
||||
unsafe { span_has_bs_avx2(src, bs, be, cpu) }
|
||||
}
|
||||
Neon(_) => span_has_bs_generic(src, bs, be),
|
||||
Generic(_) => span_has_bs_generic(src, bs, be),
|
||||
})
|
||||
}
|
||||
|
||||
/// [`span_has_bs`] for AVX2.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[target_feature(enable = "avx2,bmi2,popcnt")]
|
||||
#[inline]
|
||||
fn span_has_bs(src: &[u8], bs: usize, be: usize) -> bool {
|
||||
fn span_has_bs_avx2(src: &[u8], bs: usize, be: usize, _cpu: Avx2) -> bool {
|
||||
let mut i = bs;
|
||||
while i + 32 <= be {
|
||||
let v = unsafe { _mm256_loadu_si256(src.as_ptr().add(i) as *const __m256i) };
|
||||
let m =
|
||||
unsafe { _mm256_movemask_epi8(_mm256_cmpeq_epi8(v, _mm256_set1_epi8(b'\\' as i8))) };
|
||||
let m = _mm256_movemask_epi8(_mm256_cmpeq_epi8(v, _mm256_set1_epi8(b'\\' as i8)));
|
||||
if m != 0 {
|
||||
return true;
|
||||
}
|
||||
@@ -558,35 +566,37 @@ fn span_has_bs(src: &[u8], bs: usize, be: usize) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(not(all(
|
||||
target_arch = "x86_64",
|
||||
target_feature = "avx2",
|
||||
target_feature = "bmi2",
|
||||
target_feature = "popcnt"
|
||||
)))]
|
||||
/// [`span_has_bs`] for all other CPUs.
|
||||
#[inline]
|
||||
fn span_has_bs(src: &[u8], bs: usize, be: usize) -> bool {
|
||||
fn span_has_bs_generic(src: &[u8], bs: usize, be: usize) -> bool {
|
||||
memchr::memchr(b'\\', &src[bs..be]).is_some()
|
||||
}
|
||||
|
||||
/// Template variant of [`span_has_bs`]: a raw CR also forces the decode path.
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
target_feature = "avx2",
|
||||
target_feature = "bmi2",
|
||||
target_feature = "popcnt"
|
||||
))]
|
||||
#[inline(always)]
|
||||
fn span_has_bs_or_cr(src: &[u8], bs: usize, be: usize, cpu: impl Cpu) -> bool {
|
||||
cpu_select!(cpu, {
|
||||
Avx2(cpu) => {
|
||||
// SAFETY: `Avx2` type guarantees required target features are present
|
||||
unsafe { 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),
|
||||
})
|
||||
}
|
||||
|
||||
/// [`span_has_bs_or_cr`] for AVX2.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[target_feature(enable = "avx2,bmi2,popcnt")]
|
||||
#[inline]
|
||||
fn span_has_bs_or_cr(src: &[u8], bs: usize, be: usize) -> bool {
|
||||
fn span_has_bs_or_cr_avx2(src: &[u8], bs: usize, be: usize, _cpu: Avx2) -> bool {
|
||||
let mut i = bs;
|
||||
while i + 32 <= be {
|
||||
let v = unsafe { _mm256_loadu_si256(src.as_ptr().add(i) as *const __m256i) };
|
||||
let m = unsafe {
|
||||
_mm256_movemask_epi8(_mm256_or_si256(
|
||||
_mm256_cmpeq_epi8(v, _mm256_set1_epi8(b'\\' as i8)),
|
||||
_mm256_cmpeq_epi8(v, _mm256_set1_epi8(b'\r' as i8)),
|
||||
))
|
||||
};
|
||||
let m = _mm256_movemask_epi8(_mm256_or_si256(
|
||||
_mm256_cmpeq_epi8(v, _mm256_set1_epi8(b'\\' as i8)),
|
||||
_mm256_cmpeq_epi8(v, _mm256_set1_epi8(b'\r' as i8)),
|
||||
));
|
||||
if m != 0 {
|
||||
return true;
|
||||
}
|
||||
@@ -601,14 +611,9 @@ fn span_has_bs_or_cr(src: &[u8], bs: usize, be: usize) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(not(all(
|
||||
target_arch = "x86_64",
|
||||
target_feature = "avx2",
|
||||
target_feature = "bmi2",
|
||||
target_feature = "popcnt"
|
||||
)))]
|
||||
/// [`span_has_bs_or_cr`] for all other CPUs.
|
||||
#[inline]
|
||||
fn span_has_bs_or_cr(src: &[u8], bs: usize, be: usize) -> bool {
|
||||
fn span_has_bs_or_cr_generic(src: &[u8], bs: usize, be: usize) -> bool {
|
||||
memchr::memchr2(b'\\', b'\r', &src[bs..be]).is_some()
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user