#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum DnnArch {
Portable,
Neon,
NeonDotprod,
Avx2Fma,
AvxVnni,
Avx512Vnni,
}
#[cfg(target_arch = "aarch64")]
#[inline]
pub(crate) fn has_dotprod() -> bool {
#[cfg(target_feature = "dotprod")]
{
true
}
#[cfg(all(not(target_feature = "dotprod"), feature = "std"))]
{
std::arch::is_aarch64_feature_detected!("dotprod")
}
#[cfg(all(not(target_feature = "dotprod"), not(feature = "std")))]
{
false
}
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[inline]
fn x86_levels() -> (bool, bool, bool) {
#[cfg(feature = "std")]
{
let avx2 = std::arch::is_x86_feature_detected!("avx2")
&& std::arch::is_x86_feature_detected!("fma");
let vnni = avx2 && std::arch::is_x86_feature_detected!("avxvnni");
let vnni512 = avx2
&& std::arch::is_x86_feature_detected!("avx512f")
&& std::arch::is_x86_feature_detected!("avx512bw")
&& std::arch::is_x86_feature_detected!("avx512vnni");
(avx2, vnni, vnni512)
}
#[cfg(not(feature = "std"))]
{
let avx2 = cfg!(all(target_feature = "avx2", target_feature = "fma"));
let vnni = avx2 && cfg!(target_feature = "avxvnni");
let vnni512 = avx2
&& cfg!(all(
target_feature = "avx512f",
target_feature = "avx512bw",
target_feature = "avx512vnni"
));
(avx2, vnni, vnni512)
}
}
pub fn detect() -> DnnArch {
#[cfg(target_arch = "aarch64")]
{
if has_dotprod() { DnnArch::NeonDotprod } else { DnnArch::Neon }
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
match x86_levels() {
(true, _, true) => DnnArch::Avx512Vnni,
(true, true, false) => DnnArch::AvxVnni,
(true, false, false) => DnnArch::Avx2Fma,
_ => DnnArch::Portable,
}
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")))]
{
DnnArch::Portable
}
}
pub fn available() -> &'static [DnnArch] {
use DnnArch::*;
#[cfg(target_arch = "aarch64")]
{
if has_dotprod() { &[Portable, Neon, NeonDotprod] } else { &[Portable, Neon] }
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
match x86_levels() {
(true, true, true) => &[Portable, Avx2Fma, AvxVnni, Avx512Vnni],
(true, false, true) => &[Portable, Avx2Fma, Avx512Vnni],
(true, true, false) => &[Portable, Avx2Fma, AvxVnni],
(true, false, false) => &[Portable, Avx2Fma],
_ => &[Portable],
}
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")))]
{
&[Portable]
}
}
#[inline]
pub(crate) fn usable(arch: DnnArch) -> DnnArch {
match arch {
DnnArch::Portable => DnnArch::Portable,
#[cfg(target_arch = "aarch64")]
DnnArch::Neon => DnnArch::Neon,
#[cfg(target_arch = "aarch64")]
DnnArch::NeonDotprod => {
if has_dotprod() { DnnArch::NeonDotprod } else { DnnArch::Neon }
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
DnnArch::Avx2Fma | DnnArch::AvxVnni | DnnArch::Avx512Vnni => {
let (avx2, vnni, vnni512) = x86_levels();
match arch {
DnnArch::Avx512Vnni if vnni512 => DnnArch::Avx512Vnni,
DnnArch::AvxVnni if vnni => DnnArch::AvxVnni,
_ if avx2 => DnnArch::Avx2Fma,
_ => DnnArch::Portable,
}
}
#[allow(unreachable_patterns)]
_ => detect(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detect_is_available_and_usable() {
let best = detect();
assert!(available().contains(&best));
assert_eq!(available()[0], DnnArch::Portable);
for &a in available() {
assert_eq!(usable(a), a);
}
}
#[test]
fn every_variant_maps_to_an_available_one() {
use DnnArch::*;
for a in [Portable, Neon, NeonDotprod, Avx2Fma, AvxVnni, Avx512Vnni] {
assert!(available().contains(&usable(a)), "{a:?}");
}
}
#[cfg(all(target_arch = "aarch64", target_vendor = "apple"))]
#[test]
fn apple_targets_have_dotprod() {
assert_eq!(detect(), DnnArch::NeonDotprod);
}
}