161 lines
5.5 KiB
Rust
161 lines
5.5 KiB
Rust
//! Run-time feature detection for Aarch64 on Linux.
|
|
|
|
use super::{auxvec, cpuinfo};
|
|
use crate::detect::{bit, cache, Feature};
|
|
|
|
/// Performs run-time feature detection.
|
|
#[inline]
|
|
pub fn check_for(x: Feature) -> bool {
|
|
cache::test(x as u32, detect_features)
|
|
}
|
|
|
|
/// Try to read the features from the auxiliary vector, and if that fails, try
|
|
/// to read them from /proc/cpuinfo.
|
|
fn detect_features() -> cache::Initializer {
|
|
if let Ok(auxv) = auxvec::auxv() {
|
|
let hwcap: AtHwcap = auxv.into();
|
|
return hwcap.cache();
|
|
}
|
|
if let Ok(c) = cpuinfo::CpuInfo::new() {
|
|
let hwcap: AtHwcap = c.into();
|
|
return hwcap.cache();
|
|
}
|
|
cache::Initializer::default()
|
|
}
|
|
|
|
/// These values are part of the platform-specific [asm/hwcap.h][hwcap] .
|
|
///
|
|
/// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
|
|
struct AtHwcap {
|
|
fp: bool, // 0
|
|
asimd: bool, // 1
|
|
// evtstrm: bool, // 2
|
|
aes: bool, // 3
|
|
pmull: bool, // 4
|
|
sha1: bool, // 5
|
|
sha2: bool, // 6
|
|
crc32: bool, // 7
|
|
atomics: bool, // 8
|
|
fphp: bool, // 9
|
|
asimdhp: bool, // 10
|
|
// cpuid: bool, // 11
|
|
asimdrdm: bool, // 12
|
|
// jscvt: bool, // 13
|
|
// fcma: bool, // 14
|
|
lrcpc: bool, // 15
|
|
// dcpop: bool, // 16
|
|
// sha3: bool, // 17
|
|
// sm3: bool, // 18
|
|
// sm4: bool, // 19
|
|
asimddp: bool, // 20
|
|
// sha512: bool, // 21
|
|
sve: bool, // 22
|
|
}
|
|
|
|
impl From<auxvec::AuxVec> for AtHwcap {
|
|
/// Reads AtHwcap from the auxiliary vector.
|
|
fn from(auxv: auxvec::AuxVec) -> Self {
|
|
AtHwcap {
|
|
fp: bit::test(auxv.hwcap, 0),
|
|
asimd: bit::test(auxv.hwcap, 1),
|
|
// evtstrm: bit::test(auxv.hwcap, 2),
|
|
aes: bit::test(auxv.hwcap, 3),
|
|
pmull: bit::test(auxv.hwcap, 4),
|
|
sha1: bit::test(auxv.hwcap, 5),
|
|
sha2: bit::test(auxv.hwcap, 6),
|
|
crc32: bit::test(auxv.hwcap, 7),
|
|
atomics: bit::test(auxv.hwcap, 8),
|
|
fphp: bit::test(auxv.hwcap, 9),
|
|
asimdhp: bit::test(auxv.hwcap, 10),
|
|
// cpuid: bit::test(auxv.hwcap, 11),
|
|
asimdrdm: bit::test(auxv.hwcap, 12),
|
|
// jscvt: bit::test(auxv.hwcap, 13),
|
|
// fcma: bit::test(auxv.hwcap, 14),
|
|
lrcpc: bit::test(auxv.hwcap, 15),
|
|
// dcpop: bit::test(auxv.hwcap, 16),
|
|
// sha3: bit::test(auxv.hwcap, 17),
|
|
// sm3: bit::test(auxv.hwcap, 18),
|
|
// sm4: bit::test(auxv.hwcap, 19),
|
|
asimddp: bit::test(auxv.hwcap, 20),
|
|
// sha512: bit::test(auxv.hwcap, 21),
|
|
sve: bit::test(auxv.hwcap, 22),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<cpuinfo::CpuInfo> for AtHwcap {
|
|
/// Reads AtHwcap from /proc/cpuinfo .
|
|
fn from(c: cpuinfo::CpuInfo) -> Self {
|
|
let f = &c.field("Features");
|
|
AtHwcap {
|
|
// 64-bit names. FIXME: In 32-bit compatibility mode /proc/cpuinfo will
|
|
// map some of the 64-bit names to some 32-bit feature names. This does not
|
|
// cover that yet.
|
|
fp: f.has("fp"),
|
|
asimd: f.has("asimd"),
|
|
// evtstrm: f.has("evtstrm"),
|
|
aes: f.has("aes"),
|
|
pmull: f.has("pmull"),
|
|
sha1: f.has("sha1"),
|
|
sha2: f.has("sha2"),
|
|
crc32: f.has("crc32"),
|
|
atomics: f.has("atomics"),
|
|
fphp: f.has("fphp"),
|
|
asimdhp: f.has("asimdhp"),
|
|
// cpuid: f.has("cpuid"),
|
|
asimdrdm: f.has("asimdrdm"),
|
|
// jscvt: f.has("jscvt"),
|
|
// fcma: f.has("fcma"),
|
|
lrcpc: f.has("lrcpc"),
|
|
// dcpop: f.has("dcpop"),
|
|
// sha3: f.has("sha3"),
|
|
// sm3: f.has("sm3"),
|
|
// sm4: f.has("sm4"),
|
|
asimddp: f.has("asimddp"),
|
|
// sha512: f.has("sha512"),
|
|
sve: f.has("sve"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AtHwcap {
|
|
/// Initializes the cache from the feature -bits.
|
|
///
|
|
/// The features are enabled approximately like in LLVM host feature detection:
|
|
/// https://github.com/llvm-mirror/llvm/blob/master/lib/Support/Host.cpp#L1273
|
|
fn cache(self) -> cache::Initializer {
|
|
let mut value = cache::Initializer::default();
|
|
{
|
|
let mut enable_feature = |f, enable| {
|
|
if enable {
|
|
value.set(f as u32);
|
|
}
|
|
};
|
|
|
|
enable_feature(Feature::fp, self.fp);
|
|
// Half-float support requires float support
|
|
enable_feature(Feature::fp16, self.fp && self.fphp);
|
|
enable_feature(Feature::pmull, self.pmull);
|
|
enable_feature(Feature::crc, self.crc32);
|
|
enable_feature(Feature::lse, self.atomics);
|
|
enable_feature(Feature::rcpc, self.lrcpc);
|
|
|
|
// SIMD support requires float support - if half-floats are
|
|
// supported, it also requires half-float support:
|
|
let asimd = self.fp && self.asimd && (!self.fphp | self.asimdhp);
|
|
enable_feature(Feature::asimd, asimd);
|
|
// SIMD extensions require SIMD support:
|
|
enable_feature(Feature::rdm, self.asimdrdm && asimd);
|
|
enable_feature(Feature::dotprod, self.asimddp && asimd);
|
|
enable_feature(Feature::sve, self.sve && asimd);
|
|
|
|
// Crypto is specified as AES + PMULL + SHA1 + SHA2 per LLVM/hosts.cpp
|
|
enable_feature(
|
|
Feature::crypto,
|
|
self.aes && self.pmull && self.sha1 && self.sha2,
|
|
);
|
|
}
|
|
value
|
|
}
|
|
}
|