#![allow(unused_unsafe)]
pub(crate) mod scalar;
#[cfg(any(
all(
any(target_arch = "x86", target_arch = "x86_64"),
any(feature = "kernel-sse", feature = "kernel-avx2")
),
all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
)
))]
use crate::cpu_kernel::{CpuLevel, cpu_allows};
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
))]
pub(crate) mod neon;
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
pub(crate) mod sse2;
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-avx2"
))]
pub(crate) mod avx2_bmi2;
#[cfg(all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
))]
pub(crate) mod simd128;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum FastpathKernel {
Scalar,
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
))]
Neon,
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
Sse2,
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
Sse42,
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-avx2"
))]
Avx2Bmi2,
#[cfg(all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
))]
Simd128,
}
impl FastpathKernel {
pub(crate) fn name(self) -> &'static str {
match self {
FastpathKernel::Scalar => "scalar",
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
))]
FastpathKernel::Neon => "neon",
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
FastpathKernel::Sse2 => "sse2",
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
FastpathKernel::Sse42 => "sse4.2",
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-avx2"
))]
FastpathKernel::Avx2Bmi2 => "avx2",
#[cfg(all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
))]
FastpathKernel::Simd128 => "simd128",
}
}
}
#[inline]
pub(crate) fn select_kernel() -> FastpathKernel {
#[cfg(feature = "std")]
{
use std::sync::OnceLock;
static CACHE: OnceLock<FastpathKernel> = OnceLock::new();
*CACHE.get_or_init(detect_kernel_uncached)
}
#[cfg(not(feature = "std"))]
{
detect_kernel_uncached()
}
}
#[inline]
#[cfg_attr(
all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
),
allow(unreachable_code)
)]
fn detect_kernel_uncached() -> FastpathKernel {
#[cfg(all(
feature = "std",
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-avx2"
))]
{
if cpu_allows(CpuLevel::Avx2)
&& std::is_x86_feature_detected!("avx2")
&& std::is_x86_feature_detected!("bmi2")
{
return FastpathKernel::Avx2Bmi2;
}
}
#[cfg(all(
feature = "std",
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
{
if cpu_allows(CpuLevel::Sse42) && std::is_x86_feature_detected!("sse4.2") {
return FastpathKernel::Sse42;
}
if cpu_allows(CpuLevel::Sse2) && std::is_x86_feature_detected!("sse2") {
return FastpathKernel::Sse2;
}
}
#[cfg(all(
feature = "std",
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
))]
{
if cpu_allows(CpuLevel::Neon) && std::arch::is_aarch64_feature_detected!("neon") {
return FastpathKernel::Neon;
}
}
#[cfg(all(
not(feature = "std"),
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-avx2"
))]
{
if cpu_allows(CpuLevel::Avx2)
&& cfg!(target_feature = "avx2")
&& cfg!(target_feature = "bmi2")
{
return FastpathKernel::Avx2Bmi2;
}
}
#[cfg(all(
not(feature = "std"),
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
{
if cpu_allows(CpuLevel::Sse42) && cfg!(target_feature = "sse4.2") {
return FastpathKernel::Sse42;
}
if cpu_allows(CpuLevel::Sse2) && cfg!(target_feature = "sse2") {
return FastpathKernel::Sse2;
}
}
#[cfg(all(
not(feature = "std"),
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
))]
{
if cpu_allows(CpuLevel::Neon) && cfg!(target_feature = "neon") {
return FastpathKernel::Neon;
}
}
#[cfg(all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
))]
{
return FastpathKernel::Simd128;
}
FastpathKernel::Scalar
}
pub(crate) trait EncodeTier {
const TIER: FastpathKernel;
unsafe fn common_prefix_len_ptr(lhs: *const u8, rhs: *const u8, max: usize) -> usize;
}
macro_rules! encode_tier {
($(#[$cfg:meta])* $name:ident, $tier:ident, $prefix:path) => {
#[doc = concat!("[`FastpathKernel::", stringify!($tier), "`] as a type.")]
$(#[$cfg])*
pub(crate) struct $name;
$(#[$cfg])*
impl EncodeTier for $name {
const TIER: FastpathKernel = FastpathKernel::$tier;
#[inline(always)]
unsafe fn common_prefix_len_ptr(lhs: *const u8, rhs: *const u8, max: usize) -> usize {
unsafe { $prefix(lhs, rhs, max) }
}
}
};
}
encode_tier!(ScalarTier, Scalar, scalar::common_prefix_len_ptr);
encode_tier!(
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
))]
NeonTier,
Neon,
neon::common_prefix_len_ptr
);
encode_tier!(
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
Sse2Tier,
Sse2,
sse2::common_prefix_len_ptr
);
encode_tier!(
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
Sse42Tier,
Sse42,
sse2::common_prefix_len_ptr
);
encode_tier!(
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-avx2"
))]
Avx2Bmi2Tier,
Avx2Bmi2,
avx2_bmi2::common_prefix_len_ptr
);
encode_tier!(
#[cfg(all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
))]
Simd128Tier,
Simd128,
simd128::common_prefix_len_ptr
);
macro_rules! with_encode_tier {
($tier:expr, $k:ident => $body:expr) => {{
use $crate::encoding::fastpath as tiers;
match $tier {
tiers::FastpathKernel::Scalar => {
type $k = tiers::ScalarTier;
$body
}
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
))]
tiers::FastpathKernel::Neon => {
type $k = tiers::NeonTier;
$body
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
tiers::FastpathKernel::Sse2 => {
type $k = tiers::Sse2Tier;
$body
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
tiers::FastpathKernel::Sse42 => {
type $k = tiers::Sse42Tier;
$body
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-avx2"
))]
tiers::FastpathKernel::Avx2Bmi2 => {
type $k = tiers::Avx2Bmi2Tier;
$body
}
#[cfg(all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
))]
tiers::FastpathKernel::Simd128 => {
type $k = tiers::Simd128Tier;
$body
}
}
}};
}
pub(crate) use with_encode_tier;
macro_rules! tier_match {
($k:ty { $( $(#[$attr:meta])* $pat:pat => $arm:expr ),+ $(,)? }) => {{
const {
match <$k as $crate::encoding::fastpath::EncodeTier>::TIER {
$( $(#[$attr])* $pat => (), )+
}
};
'tier: {
$(
$(#[$attr])*
if const {
matches!(<$k as $crate::encoding::fastpath::EncodeTier>::TIER, $pat)
} {
break 'tier ($arm);
}
)+
unreachable!()
}
}};
}
pub(crate) use tier_match;
#[cfg(test)]
macro_rules! with_host_tier {
($k:ident => $body:expr) => {
$crate::encoding::fastpath::with_encode_tier!(
$crate::encoding::fastpath::select_kernel(),
$k => $body
)
};
}
#[cfg(test)]
pub(crate) use with_host_tier;
#[cfg(test)]
pub(crate) fn runnable_tiers() -> alloc::vec::Vec<FastpathKernel> {
#[allow(unused_mut)]
let mut tiers = alloc::vec![FastpathKernel::Scalar];
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-sse"
))]
{
if std::is_x86_feature_detected!("sse2") {
tiers.push(FastpathKernel::Sse2);
}
if std::is_x86_feature_detected!("sse4.2") {
tiers.push(FastpathKernel::Sse42);
}
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
feature = "kernel-avx2"
))]
if std::is_x86_feature_detected!("avx2") && std::is_x86_feature_detected!("bmi2") {
tiers.push(FastpathKernel::Avx2Bmi2);
}
#[cfg(all(
target_arch = "aarch64",
target_endian = "little",
feature = "kernel-neon"
))]
tiers.push(FastpathKernel::Neon);
#[cfg(all(
target_arch = "wasm32",
target_feature = "simd128",
feature = "kernel-simd128"
))]
tiers.push(FastpathKernel::Simd128);
tiers
}
#[cfg(test)]
pub(crate) fn same_under_every_tier<R: PartialEq + core::fmt::Debug>(
run: impl Fn(FastpathKernel) -> R,
) -> R {
let tiers = runnable_tiers();
let answer = run(tiers[0]);
for &tier in &tiers[1..] {
assert_eq!(
run(tier),
answer,
"tier {tier:?} disagrees with {:?}",
tiers[0]
);
}
answer
}
#[cfg(test)]
pub(crate) fn dyn_sink(
sink: &mut impl FnMut(crate::encoding::Sequence),
) -> &mut dyn FnMut(crate::encoding::Sequence) {
sink
}
#[cfg(test)]
mod tests;