use std::sync::OnceLock;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NeonTier {
Scalar,
Baseline,
Plus,
High,
}
impl NeonTier {
#[inline]
#[cfg_attr(not(target_arch = "aarch64"), allow(dead_code))]
pub fn huffman_fast_bits(self) -> u8 {
match self {
Self::Scalar | Self::Baseline => 10, Self::Plus => 10, Self::High => 11, }
}
#[inline]
#[cfg_attr(not(target_arch = "aarch64"), allow(dead_code))]
pub fn entropy_pair_decode(self) -> bool {
matches!(self, Self::High)
}
#[inline]
#[cfg_attr(not(target_arch = "aarch64"), allow(dead_code))]
pub fn entropy_prefetch(self) -> usize {
match self {
Self::Scalar => 0,
Self::Baseline => 192,
Self::Plus => 192,
Self::High => 256,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IntelTier {
Scalar,
Sse2,
Sse41,
Avx2,
}
#[cfg_attr(not(target_arch = "x86_64"), allow(dead_code))]
impl IntelTier {
#[inline]
pub fn huffman_fast_bits(self) -> u8 {
match self {
Self::Scalar | Self::Sse2 | Self::Sse41 => 10,
Self::Avx2 => 11,
}
}
#[inline]
pub fn entropy_prefetch(self) -> usize {
match self {
Self::Scalar => 0,
Self::Sse2 | Self::Sse41 => 192,
Self::Avx2 => 256,
}
}
#[inline]
pub fn has_sse2(self) -> bool {
!matches!(self, Self::Scalar)
}
#[inline]
pub fn has_sse41(self) -> bool {
matches!(self, Self::Sse41 | Self::Avx2)
}
#[inline]
pub fn has_avx2(self) -> bool {
matches!(self, Self::Avx2)
}
}
#[cfg_attr(not(target_arch = "aarch64"), allow(dead_code))]
pub fn neon_tier() -> NeonTier {
static TIER: OnceLock<NeonTier> = OnceLock::new();
*TIER.get_or_init(probe_neon_tier)
}
#[cfg_attr(not(target_arch = "x86_64"), allow(dead_code))]
pub fn intel_tier() -> IntelTier {
static TIER: OnceLock<IntelTier> = OnceLock::new();
*TIER.get_or_init(probe_intel_tier)
}
#[inline]
pub fn entropy_huffman_fast_bits() -> u8 {
#[cfg(target_arch = "aarch64")]
{
neon_tier().huffman_fast_bits()
}
#[cfg(target_arch = "x86_64")]
{
intel_tier().huffman_fast_bits()
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
{
10
}
}
#[inline]
pub fn entropy_pair_decode() -> bool {
#[cfg(target_arch = "aarch64")]
{
neon_tier().entropy_pair_decode()
}
#[cfg(not(target_arch = "aarch64"))]
{
false
}
}
#[inline]
pub fn entropy_prefetch_distance() -> usize {
#[cfg(target_arch = "aarch64")]
{
neon_tier().entropy_prefetch()
}
#[cfg(target_arch = "x86_64")]
{
intel_tier().entropy_prefetch()
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
{
0
}
}
fn probe_neon_tier() -> NeonTier {
if let Ok(forced) = std::env::var("EDGEFIRST_CODEC_FORCE_NEON") {
match forced.trim().to_ascii_lowercase().as_str() {
"scalar" | "off" | "0" => return NeonTier::Scalar,
"baseline" | "a53" | "neon" => return NeonTier::Baseline,
"plus" | "a55" | "dotprod" => return NeonTier::Plus,
"high" | "a76" | "a7x" | "i8mm" => return NeonTier::High,
_ => {}
}
}
#[cfg(target_arch = "aarch64")]
{
if !std::arch::is_aarch64_feature_detected!("neon") {
return NeonTier::Scalar;
}
if std::arch::is_aarch64_feature_detected!("i8mm") {
return NeonTier::High;
}
if std::arch::is_aarch64_feature_detected!("dotprod") {
if has_big_ooo_core() {
return NeonTier::High;
}
return NeonTier::Plus;
}
NeonTier::Baseline
}
#[cfg(not(target_arch = "aarch64"))]
{
NeonTier::Scalar
}
}
fn probe_intel_tier() -> IntelTier {
if let Ok(forced) = std::env::var("EDGEFIRST_CODEC_FORCE_INTEL") {
match forced.trim().to_ascii_lowercase().as_str() {
"scalar" | "off" | "0" => return IntelTier::Scalar,
"sse2" | "sse" => return IntelTier::Sse2,
"sse41" | "sse4.1" | "sse4_1" | "ssse3" => return IntelTier::Sse41,
"avx2" | "avx" => return IntelTier::Avx2,
_ => {}
}
}
#[cfg(target_arch = "x86_64")]
{
if is_x86_feature_detected!("avx2") {
return IntelTier::Avx2;
}
if is_x86_feature_detected!("sse4.1") {
return IntelTier::Sse41;
}
if is_x86_feature_detected!("sse2") {
return IntelTier::Sse2;
}
IntelTier::Scalar
}
#[cfg(not(target_arch = "x86_64"))]
{
IntelTier::Scalar
}
}
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
fn has_big_ooo_core() -> bool {
const BIG_PARTS: &[u32] = &[
0xD0B, 0xD0C, 0xD0D, 0xD40, 0xD41, 0xD42, 0xD44, 0xD47, 0xD48, 0xD4D, 0xD4E, 0xD81,
];
let Ok(entries) = std::fs::read_dir("/sys/devices/system/cpu") else {
return false;
};
for entry in entries.flatten() {
let name = entry.file_name();
let Some(name) = name.to_str() else { continue };
if !name.starts_with("cpu") || !name[3..].bytes().all(|b| b.is_ascii_digit()) {
continue;
}
let midr_path = entry.path().join("regs/identification/midr_el1");
let Ok(text) = std::fs::read_to_string(midr_path) else {
continue;
};
let text = text.trim().trim_start_matches("0x");
let Ok(midr) = u64::from_str_radix(text, 16) else {
continue;
};
let implementer = ((midr >> 24) & 0xFF) as u32;
let part = ((midr >> 4) & 0xFFF) as u32;
if implementer == 0x41 && BIG_PARTS.contains(&part) {
return true;
}
}
false
}
#[cfg(all(target_arch = "aarch64", not(target_os = "linux")))]
fn has_big_ooo_core() -> bool {
false }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn probe_neon_returns_something_sane() {
let t = probe_neon_tier();
let _ = t.huffman_fast_bits();
let _ = t.entropy_prefetch();
}
#[test]
fn probe_intel_returns_something_sane() {
let t = probe_intel_tier();
let _ = t.huffman_fast_bits();
let _ = t.entropy_prefetch();
let _ = t.has_sse2();
let _ = t.has_sse41();
let _ = t.has_avx2();
}
#[test]
fn entropy_helpers_are_stable() {
let _ = entropy_huffman_fast_bits();
let _ = entropy_prefetch_distance();
}
}