#[cfg(target_arch = "aarch64")]
fn detect_aarch64() -> Detected {
#[cfg(feature = "std")]
let caps = caps_static() | runtime_aarch64();
#[cfg(not(feature = "std"))]
let caps = caps_static();
Detected {
caps,
arch: Arch::Aarch64,
}
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android")
))]
fn hwcap_batch_aarch64() -> Caps {
use std::{fs::File, io::Read};
use crate::platform::caps::aarch64;
const AT_HWCAP: u64 = 16;
const AT_HWCAP2: u64 = 26;
const HWCAP_AES: u64 = 1 << 3;
const HWCAP_PMULL: u64 = 1 << 4;
const HWCAP_SHA2: u64 = 1 << 6;
const HWCAP_CRC32: u64 = 1 << 7;
const HWCAP_ATOMICS: u64 = 1 << 8; const HWCAP_FPHP: u64 = 1 << 9; const HWCAP_ASIMDHP: u64 = 1 << 10;
const HWCAP_SHA3: u64 = 1 << 17;
const HWCAP_SM3: u64 = 1 << 18;
const HWCAP_SM4: u64 = 1 << 19;
const HWCAP_ASIMDDP: u64 = 1 << 20; const HWCAP_SHA512: u64 = 1 << 21;
const HWCAP_SVE: u64 = 1 << 22;
const HWCAP2_SVE2: u64 = 1 << 1;
const HWCAP2_SVEAES: u64 = 1 << 2;
const HWCAP2_SVEPMULL: u64 = 1 << 3;
const HWCAP2_SVEBITPERM: u64 = 1 << 4;
const HWCAP2_SVESHA3: u64 = 1 << 5;
const HWCAP2_SVESM4: u64 = 1 << 6;
const HWCAP2_FRINT: u64 = 1 << 8; const HWCAP2_SVEI8MM: u64 = 1 << 9;
const HWCAP2_SVEF32MM: u64 = 1 << 10;
const HWCAP2_SVEF64MM: u64 = 1 << 11;
const HWCAP2_SVEBF16: u64 = 1 << 12;
const HWCAP2_I8MM: u64 = 1 << 13;
const HWCAP2_BF16: u64 = 1 << 14;
const HWCAP2_RNG: u64 = 1 << 16;
const HWCAP2_SME: u64 = 1 << 23;
const HWCAP2_SME_I16I64: u64 = 1 << 24;
const HWCAP2_SME_F64F64: u64 = 1 << 25;
const HWCAP2_SME_I8I32: u64 = 1 << 26;
const HWCAP2_SME_F16F32: u64 = 1 << 27;
const HWCAP2_SME_B16F32: u64 = 1 << 28;
const HWCAP2_SME_F32F32: u64 = 1 << 29;
const HWCAP2_SME_FA64: u64 = 1 << 30;
const HWCAP2_EBF16: u64 = 1 << 32;
const HWCAP2_SVE_EBF16: u64 = 1 << 33;
const HWCAP2_SVE2P1: u64 = 1 << 36;
const HWCAP2_SME2: u64 = 1 << 37;
const HWCAP2_SME2P1: u64 = 1 << 38;
const HWCAP2_SME_I16I32: u64 = 1 << 39;
const HWCAP2_SME_BI32I32: u64 = 1 << 40;
const HWCAP2_SME_B16B16: u64 = 1 << 41;
const HWCAP2_SME_F16F16: u64 = 1 << 42;
const HWCAP2_MOPS: u64 = 1 << 43;
const HWCAP2_SVE_B16B16: u64 = 1 << 45;
const HWCAP2_LSE128: u64 = 1 << 47;
let (hwcap, hwcap2) = (|| -> Option<(u64, u64)> {
let mut file = File::open("/proc/self/auxv").ok()?;
let mut buf = [0u8; 4096]; let n = file.read(&mut buf).ok()?;
let mut hwcap = 0u64;
let mut hwcap2 = 0u64;
let entries = buf.get(..n)?;
for chunk in entries.chunks_exact(16) {
let a_type = u64::from_ne_bytes(chunk.get(0..8)?.try_into().ok()?);
let a_val = u64::from_ne_bytes(chunk.get(8..16)?.try_into().ok()?);
if a_type == AT_HWCAP {
hwcap = a_val;
} else if a_type == AT_HWCAP2 {
hwcap2 = a_val;
} else if a_type == 0 {
break;
}
}
Some((hwcap, hwcap2))
})()
.unwrap_or((0, 0));
let mut caps = Caps::NONE;
if hwcap & HWCAP_AES != 0 {
caps |= aarch64::AES;
}
if hwcap & HWCAP_PMULL != 0 {
caps |= aarch64::PMULL;
}
if hwcap & HWCAP_SHA2 != 0 {
caps |= aarch64::SHA2;
}
if hwcap & HWCAP_CRC32 != 0 {
caps |= aarch64::CRC;
}
if hwcap & HWCAP_ATOMICS != 0 {
caps |= aarch64::LSE;
}
if hwcap & (HWCAP_FPHP | HWCAP_ASIMDHP) != 0 {
caps |= aarch64::FP16;
}
if hwcap & HWCAP_SHA3 != 0 {
caps |= aarch64::SHA3;
}
if hwcap & HWCAP_SM3 != 0 {
caps |= aarch64::SM3;
}
if hwcap & HWCAP_SM4 != 0 {
caps |= aarch64::SM4;
}
if hwcap & HWCAP_ASIMDDP != 0 {
caps |= aarch64::DOTPROD;
}
if hwcap & HWCAP_SHA512 != 0 {
caps |= aarch64::SHA512;
}
if hwcap & HWCAP_SVE != 0 {
caps |= aarch64::SVE;
}
if hwcap2 & HWCAP2_SVE2 != 0 {
caps |= aarch64::SVE2;
}
if hwcap2 & HWCAP2_SVEAES != 0 {
caps |= aarch64::SVE2_AES;
}
if hwcap2 & HWCAP2_SVEPMULL != 0 {
caps |= aarch64::SVE2_PMULL;
}
if hwcap2 & HWCAP2_SVEBITPERM != 0 {
caps |= aarch64::SVE2_BITPERM;
}
if hwcap2 & HWCAP2_SVESHA3 != 0 {
caps |= aarch64::SVE2_SHA3;
}
if hwcap2 & HWCAP2_SVESM4 != 0 {
caps |= aarch64::SVE2_SM4;
}
if hwcap2 & HWCAP2_FRINT != 0 {
caps |= aarch64::FRINTTS;
}
if hwcap2 & HWCAP2_SVEI8MM != 0 {
caps |= aarch64::SVE2_I8MM;
}
if hwcap2 & HWCAP2_SVEF32MM != 0 {
caps |= aarch64::SVE2_F32MM;
}
if hwcap2 & HWCAP2_SVEF64MM != 0 {
caps |= aarch64::SVE2_F64MM;
}
if hwcap2 & HWCAP2_SVEBF16 != 0 {
caps |= aarch64::SVE2_BF16;
}
if hwcap2 & HWCAP2_I8MM != 0 {
caps |= aarch64::I8MM;
}
if hwcap2 & HWCAP2_BF16 != 0 {
caps |= aarch64::BF16;
}
if hwcap2 & HWCAP2_RNG != 0 {
caps |= aarch64::RNG;
}
if hwcap2 & HWCAP2_SME != 0 {
caps |= aarch64::SME;
}
if hwcap2 & HWCAP2_SME_I16I64 != 0 {
caps |= aarch64::SME_I16I64;
}
if hwcap2 & HWCAP2_SME_F64F64 != 0 {
caps |= aarch64::SME_F64F64;
}
if hwcap2 & HWCAP2_SME_I8I32 != 0 {
caps |= aarch64::SME_I8I32;
}
if hwcap2 & HWCAP2_SME_F16F32 != 0 {
caps |= aarch64::SME_F16F32;
}
if hwcap2 & HWCAP2_SME_B16F32 != 0 {
caps |= aarch64::SME_B16F32;
}
if hwcap2 & HWCAP2_SME_F32F32 != 0 {
caps |= aarch64::SME_F32F32;
}
if hwcap2 & HWCAP2_SME_FA64 != 0 {
caps |= aarch64::SME_FA64;
}
if hwcap2 & HWCAP2_EBF16 != 0 {
caps |= aarch64::EBF16;
}
if hwcap2 & HWCAP2_SVE_EBF16 != 0 {
caps |= aarch64::SVE2_EBF16;
}
if hwcap2 & HWCAP2_SVE2P1 != 0 {
caps |= aarch64::SVE2P1;
}
if hwcap2 & HWCAP2_SME2 != 0 {
caps |= aarch64::SME2;
}
if hwcap2 & HWCAP2_SME2P1 != 0 {
caps |= aarch64::SME2P1;
}
if hwcap2 & HWCAP2_SME_I16I32 != 0 {
caps |= aarch64::SME_I16I32;
}
if hwcap2 & HWCAP2_SME_BI32I32 != 0 {
caps |= aarch64::SME_BI32I32;
}
if hwcap2 & HWCAP2_SME_B16B16 != 0 {
caps |= aarch64::SME_B16B16;
}
if hwcap2 & HWCAP2_SME_F16F16 != 0 {
caps |= aarch64::SME_F16F16;
}
if hwcap2 & HWCAP2_MOPS != 0 {
caps |= aarch64::MOPS;
}
if hwcap2 & HWCAP2_SVE_B16B16 != 0 {
caps |= aarch64::SVE_B16B16;
}
if hwcap2 & HWCAP2_LSE128 != 0 {
caps |= aarch64::LSE2;
}
caps
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android")
))]
fn runtime_aarch64() -> Caps {
hwcap_batch_aarch64()
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
not(any(target_os = "linux", target_os = "android"))
))]
fn runtime_aarch64() -> Caps {
use crate::platform::caps::aarch64;
let mut caps = Caps::NONE;
if std::arch::is_aarch64_feature_detected!("aes") {
caps |= aarch64::AES;
}
if std::arch::is_aarch64_feature_detected!("pmull") {
caps |= aarch64::PMULL;
}
if std::arch::is_aarch64_feature_detected!("sha2") {
caps |= aarch64::SHA2;
}
if std::arch::is_aarch64_feature_detected!("sha3") {
caps |= aarch64::SHA3 | aarch64::SHA512;
}
if std::arch::is_aarch64_feature_detected!("sm4") {
caps |= aarch64::SM3 | aarch64::SM4;
}
if std::arch::is_aarch64_feature_detected!("crc") {
caps |= aarch64::CRC;
}
if std::arch::is_aarch64_feature_detected!("dotprod") {
caps |= aarch64::DOTPROD;
}
if std::arch::is_aarch64_feature_detected!("fp16") {
caps |= aarch64::FP16;
}
if std::arch::is_aarch64_feature_detected!("i8mm") {
caps |= aarch64::I8MM;
}
if std::arch::is_aarch64_feature_detected!("bf16") {
caps |= aarch64::BF16;
}
if std::arch::is_aarch64_feature_detected!("frintts") {
caps |= aarch64::FRINTTS;
}
if std::arch::is_aarch64_feature_detected!("sve") {
caps |= aarch64::SVE;
}
if std::arch::is_aarch64_feature_detected!("sve2") {
caps |= aarch64::SVE2;
}
if std::arch::is_aarch64_feature_detected!("lse") {
caps |= aarch64::LSE;
}
if std::arch::is_aarch64_feature_detected!("lse2") {
caps |= aarch64::LSE2;
}
#[cfg(all(target_feature = "mops", not(any(target_os = "linux", target_os = "android"))))]
{
caps |= aarch64::MOPS;
}
if std::arch::is_aarch64_feature_detected!("rand") {
caps |= aarch64::RNG;
}
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
{
caps |= detect_apple_sme_features();
}
#[cfg(target_os = "linux")]
{
if std::arch::is_aarch64_feature_detected!("sme") {
caps |= aarch64::SME;
}
if std::arch::is_aarch64_feature_detected!("sme2") {
caps |= aarch64::SME2;
}
}
caps
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos")
))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum AppleSiliconGen {
M1,
M2,
M3,
M4,
M5,
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
#[cfg_attr(miri, allow(dead_code))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Aarch64TuneFamily {
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
AppleM1M3,
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
AppleM4M5,
#[cfg(any(target_os = "linux", target_os = "android"))]
Graviton2,
#[cfg(any(target_os = "linux", target_os = "android"))]
Graviton3,
#[cfg(any(target_os = "linux", target_os = "android"))]
Graviton4,
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos")
))]
fn detect_apple_silicon_gen() -> Option<AppleSiliconGen> {
const CPUFAMILY_ARM_FIRESTORM_ICESTORM: u32 = 0x1b58_8bb3; const CPUFAMILY_ARM_BLIZZARD_AVALANCHE: u32 = 0xda33_d83d; const CPUFAMILY_ARM_EVEREST_SAWTOOTH: u32 = 0x8765_edea; const CPUFAMILY_ARM_COLL: u32 = 0x2876_f5b5; const CPUFAMILY_ARM_IBIZA: u32 = 0xfa33_415e; const CPUFAMILY_ARM_LOBOS: u32 = 0x5f4d_ea93; const CPUFAMILY_ARM_PALMA: u32 = 0x7201_5832; const CPUFAMILY_ARM_DONAN: u32 = 0x6f51_29ac; const CPUFAMILY_ARM_BRAVA: u32 = 0x17d5_b93a; const CPUFAMILY_ARM_TAHITI: u32 = 0x75d4_acb9; const CPUFAMILY_ARM_TUPAI: u32 = 0x2045_26d0;
const CPUFAMILY_ARM_HIDRA: u32 = 0x1d5a_87e8; const CPUFAMILY_ARM_SOTRA: u32 = 0xf76c_5b1a; const CPUFAMILY_ARM_TILOS: u32 = 0x01d7_a72b; const CPUFAMILY_ARM_THERA: u32 = 0xab34_5f09;
#[allow(unsafe_code)]
unsafe extern "C" {
fn sysctlbyname(
name: *const u8,
oldp: *mut core::ffi::c_void,
oldlenp: *mut usize,
newp: *const core::ffi::c_void,
newlen: usize,
) -> i32;
}
let mut cpufamily: u32 = 0;
let mut size = core::mem::size_of::<u32>();
#[allow(unsafe_code)]
let ret = unsafe {
sysctlbyname(
c"hw.cpufamily".as_ptr().cast(),
core::ptr::addr_of_mut!(cpufamily).cast(),
core::ptr::addr_of_mut!(size),
core::ptr::null(),
0,
)
};
if ret != 0 {
return None;
}
match cpufamily {
CPUFAMILY_ARM_FIRESTORM_ICESTORM => Some(AppleSiliconGen::M1),
CPUFAMILY_ARM_BLIZZARD_AVALANCHE | CPUFAMILY_ARM_EVEREST_SAWTOOTH => Some(AppleSiliconGen::M2),
CPUFAMILY_ARM_IBIZA | CPUFAMILY_ARM_LOBOS | CPUFAMILY_ARM_PALMA => Some(AppleSiliconGen::M3),
CPUFAMILY_ARM_DONAN | CPUFAMILY_ARM_BRAVA => Some(AppleSiliconGen::M4),
CPUFAMILY_ARM_HIDRA | CPUFAMILY_ARM_SOTRA => Some(AppleSiliconGen::M5),
CPUFAMILY_ARM_COLL => Some(AppleSiliconGen::M2), CPUFAMILY_ARM_TAHITI | CPUFAMILY_ARM_TUPAI => Some(AppleSiliconGen::M4), CPUFAMILY_ARM_TILOS | CPUFAMILY_ARM_THERA => Some(AppleSiliconGen::M5), _ => None, }
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
#[must_use]
#[cfg_attr(miri, allow(dead_code))]
pub(crate) fn detect_aarch64_tune_family() -> Option<Aarch64TuneFamily> {
#[cfg(all(
feature = "std",
any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos")
))]
if let Some(chip_gen) = detect_apple_silicon_gen() {
return Some(match chip_gen {
AppleSiliconGen::M1 | AppleSiliconGen::M2 | AppleSiliconGen::M3 => Aarch64TuneFamily::AppleM1M3,
AppleSiliconGen::M4 | AppleSiliconGen::M5 => Aarch64TuneFamily::AppleM4M5,
});
}
#[cfg(all(feature = "std", any(target_os = "linux", target_os = "android")))]
if let Some(family) = detect_linux_aarch64_tune_family() {
return Some(family);
}
None
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos")
))]
fn detect_apple_sme_features() -> Caps {
use crate::platform::caps::aarch64;
fn sysctl_u32(name: &[u8]) -> u32 {
#[allow(unsafe_code)]
unsafe extern "C" {
fn sysctlbyname(
name: *const u8,
oldp: *mut core::ffi::c_void,
oldlenp: *mut usize,
newp: *const core::ffi::c_void,
newlen: usize,
) -> i32;
}
let mut value: u32 = 0;
let mut size = core::mem::size_of::<u32>();
#[allow(unsafe_code)]
let ret = unsafe {
sysctlbyname(
name.as_ptr(),
core::ptr::addr_of_mut!(value).cast(),
core::ptr::addr_of_mut!(size),
core::ptr::null(),
0,
)
};
if ret == 0 { value } else { 0 }
}
let mut caps = Caps::NONE;
if sysctl_u32(c"hw.optional.arm.FEAT_SME".to_bytes_with_nul()) != 0 {
caps |= aarch64::SME;
}
if sysctl_u32(c"hw.optional.arm.FEAT_SME2".to_bytes_with_nul()) != 0 {
caps |= aarch64::SME2;
}
if sysctl_u32(c"hw.optional.arm.FEAT_SME2p1".to_bytes_with_nul()) != 0 {
caps |= aarch64::SME2P1;
}
if sysctl_u32(c"hw.optional.arm.FEAT_SME_I16I64".to_bytes_with_nul()) != 0 {
caps |= aarch64::SME_I16I64;
}
if sysctl_u32(c"hw.optional.arm.FEAT_SME_F64F64".to_bytes_with_nul()) != 0 {
caps |= aarch64::SME_F64F64;
}
if sysctl_u32(c"hw.optional.arm.FEAT_SME_B16B16".to_bytes_with_nul()) != 0 {
caps |= aarch64::SME_B16B16;
}
if sysctl_u32(c"hw.optional.arm.FEAT_SME_F16F16".to_bytes_with_nul()) != 0 {
caps |= aarch64::SME_F16F16;
}
if caps.is_empty()
&& let Some(chip_gen) = detect_apple_silicon_gen()
{
match chip_gen {
AppleSiliconGen::M4 => {
caps |= aarch64::SME;
}
AppleSiliconGen::M5 => {
caps |= aarch64::SME | aarch64::SME2 | aarch64::SME2P1 | aarch64::SME_B16B16 | aarch64::SME_F16F16;
}
_ => {}
}
}
caps
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos")
))]
#[allow(dead_code)] fn detect_apple_sme_tile_size() -> u16 {
fn sysctl_u32(name: &[u8]) -> u32 {
#[allow(unsafe_code)]
unsafe extern "C" {
fn sysctlbyname(
name: *const u8,
oldp: *mut core::ffi::c_void,
oldlenp: *mut usize,
newp: *const core::ffi::c_void,
newlen: usize,
) -> i32;
}
let mut value: u32 = 0;
let mut size = core::mem::size_of::<u32>();
#[allow(unsafe_code)]
let ret = unsafe {
sysctlbyname(
name.as_ptr(),
core::ptr::addr_of_mut!(value).cast(),
core::ptr::addr_of_mut!(size),
core::ptr::null(),
0,
)
};
if ret == 0 { value } else { 0 }
}
let svl_bytes = sysctl_u32(c"hw.optional.arm.sme_max_svl_b".to_bytes_with_nul());
if svl_bytes > 0 {
return svl_bytes as u16;
}
if let Some(AppleSiliconGen::M4 | AppleSiliconGen::M5) = detect_apple_silicon_gen() {
return 16; }
0 }
#[cfg(all(target_arch = "aarch64", target_os = "linux", feature = "std"))]
#[allow(dead_code)] fn detect_sve_vlen() -> u16 {
const SYS_PRCTL: u64 = 167;
const PR_SVE_GET_VL: u64 = 51;
const PR_SVE_VL_LEN_MASK: u64 = 0xFFFF;
let result: i64;
#[allow(unsafe_code)]
unsafe {
core::arch::asm!(
"svc #0",
in("x8") SYS_PRCTL,
in("x0") PR_SVE_GET_VL,
in("x1") 0u64,
in("x2") 0u64,
in("x3") 0u64,
in("x4") 0u64,
lateout("x0") result,
options(nostack)
);
}
if result < 0 {
return 0; }
let vl_bytes = (result as u64) & PR_SVE_VL_LEN_MASK;
let vl_bits = vl_bytes.strict_mul(8);
if vl_bits > u16::MAX as u64 {
u16::MAX
} else {
vl_bits as u16
}
}
#[cfg(all(target_arch = "aarch64", not(all(target_os = "linux", feature = "std"))))]
#[allow(dead_code)] fn detect_sve_vlen() -> u16 {
0
}
#[cfg(all(target_arch = "aarch64", target_os = "linux", feature = "std"))]
#[allow(dead_code)] fn detect_sme_vlen() -> u16 {
const SYS_PRCTL: u64 = 167;
const PR_SME_GET_VL: u64 = 63;
const PR_SME_VL_LEN_MASK: u64 = 0xFFFF;
let result: i64;
#[allow(unsafe_code)]
unsafe {
core::arch::asm!(
"svc #0",
in("x8") SYS_PRCTL,
in("x0") PR_SME_GET_VL,
in("x1") 0u64,
in("x2") 0u64,
in("x3") 0u64,
in("x4") 0u64,
lateout("x0") result,
options(nostack)
);
}
if result < 0 {
return 0; }
let vl_bytes = (result as u64) & PR_SME_VL_LEN_MASK;
let vl_bits = vl_bytes.strict_mul(8);
if vl_bits > u16::MAX as u64 {
u16::MAX
} else {
vl_bits as u16
}
}
#[cfg(all(target_arch = "aarch64", not(all(target_os = "linux", feature = "std"))))]
#[allow(dead_code)] fn detect_sme_vlen() -> u16 {
0
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android"),
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
const MIDR_IMPLEMENTER_SHIFT: u32 = 24;
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android"),
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
const MIDR_PARTNUM_SHIFT: u32 = 4;
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android"),
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
const ARM_CPU_IMP_ARM: u32 = 0x41;
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android"),
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
const ARM_CPU_PART_NEOVERSE_N1: u32 = 0xD0C;
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android"),
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
const ARM_CPU_PART_NEOVERSE_V1: u32 = 0xD40;
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android"),
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
const ARM_CPU_PART_NEOVERSE_V2: u32 = 0xD4F;
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android"),
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
#[inline]
fn midr_implementer(midr: u32) -> u32 {
midr >> MIDR_IMPLEMENTER_SHIFT
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android"),
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
#[inline]
fn midr_partnum(midr: u32) -> u32 {
(midr >> MIDR_PARTNUM_SHIFT) & 0x0fff
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android"),
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
fn parse_u32_auto_radix(value: &str) -> Option<u32> {
let value = value.trim();
if let Some(hex) = value.strip_prefix("0x").or_else(|| value.strip_prefix("0X")) {
return u32::from_str_radix(hex, 16).ok();
}
value.parse::<u32>().ok().or_else(|| u32::from_str_radix(value, 16).ok())
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android"),
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
fn read_linux_midr_sysfs() -> Option<u32> {
let paths = [
"/sys/devices/system/cpu/cpu0/regs/identification/midr_el1",
"/sys/devices/system/cpu/cpu0/identification/midr_el1",
];
for path in paths {
if let Ok(text) = std::fs::read_to_string(path)
&& let Some(midr) = parse_u32_auto_radix(&text)
{
return Some(midr);
}
}
None
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android"),
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
fn read_linux_midr_cpuinfo() -> Option<u32> {
let cpuinfo = std::fs::read_to_string("/proc/cpuinfo").ok()?;
let mut implementer = None;
let mut part = None;
for line in cpuinfo.lines() {
let Some((key, value)) = line.split_once(':') else {
continue;
};
let key = key.trim();
let value = value.trim();
if key.eq_ignore_ascii_case("CPU implementer") {
implementer = parse_u32_auto_radix(value);
} else if key.eq_ignore_ascii_case("CPU part") {
part = parse_u32_auto_radix(value);
}
if implementer.is_some() && part.is_some() {
break;
}
}
Some((implementer? << MIDR_IMPLEMENTER_SHIFT) | (0xF << 16) | (part? << MIDR_PARTNUM_SHIFT))
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android"),
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
#[inline]
fn map_linux_midr_to_tune_family(midr: u32) -> Option<Aarch64TuneFamily> {
if midr_implementer(midr) != ARM_CPU_IMP_ARM {
return None;
}
match midr_partnum(midr) {
ARM_CPU_PART_NEOVERSE_N1 => Some(Aarch64TuneFamily::Graviton2),
ARM_CPU_PART_NEOVERSE_V1 => Some(Aarch64TuneFamily::Graviton3),
ARM_CPU_PART_NEOVERSE_V2 => Some(Aarch64TuneFamily::Graviton4),
_ => None,
}
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(target_os = "linux", target_os = "android"),
any(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64")
))]
fn detect_linux_aarch64_tune_family() -> Option<Aarch64TuneFamily> {
let midr = read_linux_midr_sysfs().or_else(read_linux_midr_cpuinfo)?;
map_linux_midr_to_tune_family(midr)
}