use std::fmt;
use std::sync::LazyLock;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum SimdSupport {
None,
Neon,
Sse,
Avx,
AvxFma,
Avx2,
Avx512,
Avx512FP16,
Lsx,
Lasx,
}
impl fmt::Display for SimdSupport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
Self::None => "none",
Self::Neon => "neon",
Self::Sse => "sse",
Self::Avx => "avx",
Self::AvxFma => "avx_fma",
Self::Avx2 => "avx2",
Self::Avx512 => "avx512",
Self::Avx512FP16 => "avx512_fp16",
Self::Lsx => "lsx",
Self::Lasx => "lasx",
};
f.write_str(name)
}
}
#[derive(Debug, Clone)]
pub struct SimdInfo {
pub tier: SimdSupport,
pub target_arch: &'static str,
pub host_features: Vec<&'static str>,
}
pub fn simd_info() -> SimdInfo {
SimdInfo {
tier: *SIMD_SUPPORT,
target_arch: std::env::consts::ARCH,
host_features: detect_host_features(),
}
}
#[cfg(target_arch = "x86_64")]
fn detect_host_features() -> Vec<&'static str> {
let mut features = Vec::with_capacity(17);
if is_x86_feature_detected!("sse2") {
features.push("sse2");
}
if is_x86_feature_detected!("sse3") {
features.push("sse3");
}
if is_x86_feature_detected!("ssse3") {
features.push("ssse3");
}
if is_x86_feature_detected!("sse4.1") {
features.push("sse4.1");
}
if is_x86_feature_detected!("sse4.2") {
features.push("sse4.2");
}
if is_x86_feature_detected!("popcnt") {
features.push("popcnt");
}
if is_x86_feature_detected!("avx") {
features.push("avx");
}
if is_x86_feature_detected!("avx2") {
features.push("avx2");
}
if is_x86_feature_detected!("fma") {
features.push("fma");
}
if is_x86_feature_detected!("f16c") {
features.push("f16c");
}
if is_x86_feature_detected!("bmi1") {
features.push("bmi1");
}
if is_x86_feature_detected!("bmi2") {
features.push("bmi2");
}
if is_x86_feature_detected!("avx512f") {
features.push("avx512f");
}
if is_x86_feature_detected!("avx512bw") {
features.push("avx512bw");
}
if is_x86_feature_detected!("avx512cd") {
features.push("avx512cd");
}
if is_x86_feature_detected!("avx512dq") {
features.push("avx512dq");
}
if is_x86_feature_detected!("avx512vl") {
features.push("avx512vl");
}
features
}
#[cfg(not(target_arch = "x86_64"))]
fn detect_host_features() -> Vec<&'static str> {
Vec::new()
}
pub static SIMD_SUPPORT: LazyLock<SimdSupport> = LazyLock::new(|| {
#[cfg(all(target_arch = "aarch64", any(target_os = "ios", target_os = "tvos")))]
{
SimdSupport::Neon
}
#[cfg(all(
target_arch = "aarch64",
not(any(target_os = "ios", target_os = "tvos"))
))]
{
if aarch64::has_neon_f16_support() {
SimdSupport::Neon
} else {
SimdSupport::None
}
}
#[cfg(target_arch = "x86_64")]
{
if x86::has_avx512() {
if x86::has_avx512_f16_support() {
SimdSupport::Avx512FP16
} else {
SimdSupport::Avx512
}
} else if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("fma") {
SimdSupport::Avx2
} else if is_x86_feature_detected!("avx") && is_x86_feature_detected!("fma") {
SimdSupport::AvxFma
} else if is_x86_feature_detected!("avx") {
SimdSupport::Avx
} else {
SimdSupport::None
}
}
#[cfg(target_arch = "loongarch64")]
{
if loongarch64::has_lasx_support() {
SimdSupport::Lasx
} else if loongarch64::has_lsx_support() {
SimdSupport::Lsx
} else {
SimdSupport::None
}
}
});
#[cfg(target_arch = "x86_64")]
mod x86 {
use core::arch::x86_64::__cpuid;
#[inline]
fn check_flag(x: usize, position: u32) -> bool {
x & (1 << position) != 0
}
pub fn has_avx512_f16_support() -> bool {
if !has_avx512() {
return false;
}
#[allow(unused_unsafe)]
let ext_cpuid_result = unsafe { __cpuid(7) };
check_flag(ext_cpuid_result.edx as usize, 23)
}
pub fn has_avx512() -> bool {
is_x86_feature_detected!("avx512f")
}
}
#[cfg(all(target_arch = "aarch64", target_os = "macos"))]
mod aarch64 {
pub fn has_neon_f16_support() -> bool {
true
}
}
#[cfg(all(target_arch = "aarch64", target_os = "linux"))]
mod aarch64 {
pub fn has_neon_f16_support() -> bool {
let flags = unsafe { libc::getauxval(libc::AT_HWCAP) };
flags & libc::HWCAP_FPHP != 0
}
}
#[cfg(all(target_arch = "aarch64", target_os = "windows"))]
mod aarch64 {
pub fn has_neon_f16_support() -> bool {
false
}
}
#[cfg(target_arch = "loongarch64")]
mod loongarch64 {
pub fn has_lsx_support() -> bool {
let flags = unsafe { libc::getauxval(libc::AT_HWCAP) };
flags & libc::HWCAP_LOONGARCH_LSX != 0
}
pub fn has_lasx_support() -> bool {
let flags = unsafe { libc::getauxval(libc::AT_HWCAP) };
flags & libc::HWCAP_LOONGARCH_LASX != 0
}
}
#[cfg(all(target_arch = "aarch64", target_os = "android"))]
mod aarch64 {
pub fn has_neon_f16_support() -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
#[test]
fn simd_info_exposes_tier() {
let info = simd_info();
assert_eq!(info.target_arch, std::env::consts::ARCH);
assert_eq!(info.tier, *SIMD_SUPPORT);
}
#[cfg(target_arch = "x86_64")]
#[test]
fn simd_info_features_include_baseline() {
let info = simd_info();
assert!(info.host_features.contains(&"sse2"));
}
#[cfg(not(target_arch = "x86_64"))]
#[test]
fn simd_info_features_empty_off_x86_64() {
let info = simd_info();
assert!(info.host_features.is_empty());
}
#[cfg(target_arch = "x86_64")]
#[test]
fn avx_fma_tiers_are_only_selected_when_fma_is_detected() {
if matches!(*SIMD_SUPPORT, SimdSupport::Avx2 | SimdSupport::AvxFma) {
assert!(
is_x86_feature_detected!("fma"),
"tier {} dispatches to avx,fma kernels but the host has no FMA",
*SIMD_SUPPORT
);
}
}
#[rstest]
#[case::none(SimdSupport::None, "none")]
#[case::neon(SimdSupport::Neon, "neon")]
#[case::sse(SimdSupport::Sse, "sse")]
#[case::avx(SimdSupport::Avx, "avx")]
#[case::avx_fma(SimdSupport::AvxFma, "avx_fma")]
#[case::avx2(SimdSupport::Avx2, "avx2")]
#[case::avx512(SimdSupport::Avx512, "avx512")]
#[case::avx512_fp16(SimdSupport::Avx512FP16, "avx512_fp16")]
#[case::lsx(SimdSupport::Lsx, "lsx")]
#[case::lasx(SimdSupport::Lasx, "lasx")]
fn simd_support_display_matches_lowercase_convention(
#[case] tier: SimdSupport,
#[case] expected: &str,
) {
assert_eq!(tier.to_string(), expected);
}
}