| //! Run-time feature detection for Aarch64 on Linux. |
| |
| use super::{auxvec, cpuinfo}; |
| use crate::detect::{bit, cache, Feature}; |
| |
| /// Try to read the features from the auxiliary vector, and if that fails, try |
| /// to read them from /proc/cpuinfo. |
| pub(crate) 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 |
| } |
| } |