1 //! Run-time feature detection for Aarch64 on Linux.
2 
3 use super::{auxvec, cpuinfo};
4 use crate::detect::{bit, cache, Feature};
5 
6 /// Performs run-time feature detection.
7 #[inline]
check_for(x: Feature) -> bool8 pub fn check_for(x: Feature) -> bool {
9     cache::test(x as u32, detect_features)
10 }
11 
12 /// Try to read the features from the auxiliary vector, and if that fails, try
13 /// to read them from /proc/cpuinfo.
detect_features() -> cache::Initializer14 fn detect_features() -> cache::Initializer {
15     if let Ok(auxv) = auxvec::auxv() {
16         let hwcap: AtHwcap = auxv.into();
17         return hwcap.cache();
18     }
19     if let Ok(c) = cpuinfo::CpuInfo::new() {
20         let hwcap: AtHwcap = c.into();
21         return hwcap.cache();
22     }
23     cache::Initializer::default()
24 }
25 
26 /// These values are part of the platform-specific [asm/hwcap.h][hwcap] .
27 ///
28 /// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
29 struct AtHwcap {
30     fp: bool,    // 0
31     asimd: bool, // 1
32     // evtstrm: bool, // 2
33     aes: bool,     // 3
34     pmull: bool,   // 4
35     sha1: bool,    // 5
36     sha2: bool,    // 6
37     crc32: bool,   // 7
38     atomics: bool, // 8
39     fphp: bool,    // 9
40     asimdhp: bool, // 10
41     // cpuid: bool, // 11
42     asimdrdm: bool, // 12
43     // jscvt: bool, // 13
44     // fcma: bool, // 14
45     lrcpc: bool, // 15
46     // dcpop: bool, // 16
47     // sha3: bool, // 17
48     // sm3: bool, // 18
49     // sm4: bool, // 19
50     asimddp: bool, // 20
51     // sha512: bool, // 21
52     sve: bool, // 22
53 }
54 
55 impl From<auxvec::AuxVec> for AtHwcap {
56     /// Reads AtHwcap from the auxiliary vector.
from(auxv: auxvec::AuxVec) -> Self57     fn from(auxv: auxvec::AuxVec) -> Self {
58         AtHwcap {
59             fp: bit::test(auxv.hwcap, 0),
60             asimd: bit::test(auxv.hwcap, 1),
61             // evtstrm: bit::test(auxv.hwcap, 2),
62             aes: bit::test(auxv.hwcap, 3),
63             pmull: bit::test(auxv.hwcap, 4),
64             sha1: bit::test(auxv.hwcap, 5),
65             sha2: bit::test(auxv.hwcap, 6),
66             crc32: bit::test(auxv.hwcap, 7),
67             atomics: bit::test(auxv.hwcap, 8),
68             fphp: bit::test(auxv.hwcap, 9),
69             asimdhp: bit::test(auxv.hwcap, 10),
70             // cpuid: bit::test(auxv.hwcap, 11),
71             asimdrdm: bit::test(auxv.hwcap, 12),
72             // jscvt: bit::test(auxv.hwcap, 13),
73             // fcma: bit::test(auxv.hwcap, 14),
74             lrcpc: bit::test(auxv.hwcap, 15),
75             // dcpop: bit::test(auxv.hwcap, 16),
76             // sha3: bit::test(auxv.hwcap, 17),
77             // sm3: bit::test(auxv.hwcap, 18),
78             // sm4: bit::test(auxv.hwcap, 19),
79             asimddp: bit::test(auxv.hwcap, 20),
80             // sha512: bit::test(auxv.hwcap, 21),
81             sve: bit::test(auxv.hwcap, 22),
82         }
83     }
84 }
85 
86 impl From<cpuinfo::CpuInfo> for AtHwcap {
87     /// Reads AtHwcap from /proc/cpuinfo .
from(c: cpuinfo::CpuInfo) -> Self88     fn from(c: cpuinfo::CpuInfo) -> Self {
89         let f = &c.field("Features");
90         AtHwcap {
91             // 64-bit names. FIXME: In 32-bit compatibility mode /proc/cpuinfo will
92             // map some of the 64-bit names to some 32-bit feature names. This does not
93             // cover that yet.
94             fp: f.has("fp"),
95             asimd: f.has("asimd"),
96             // evtstrm: f.has("evtstrm"),
97             aes: f.has("aes"),
98             pmull: f.has("pmull"),
99             sha1: f.has("sha1"),
100             sha2: f.has("sha2"),
101             crc32: f.has("crc32"),
102             atomics: f.has("atomics"),
103             fphp: f.has("fphp"),
104             asimdhp: f.has("asimdhp"),
105             // cpuid: f.has("cpuid"),
106             asimdrdm: f.has("asimdrdm"),
107             // jscvt: f.has("jscvt"),
108             // fcma: f.has("fcma"),
109             lrcpc: f.has("lrcpc"),
110             // dcpop: f.has("dcpop"),
111             // sha3: f.has("sha3"),
112             // sm3: f.has("sm3"),
113             // sm4: f.has("sm4"),
114             asimddp: f.has("asimddp"),
115             // sha512: f.has("sha512"),
116             sve: f.has("sve"),
117         }
118     }
119 }
120 
121 impl AtHwcap {
122     /// Initializes the cache from the feature -bits.
123     ///
124     /// The features are enabled approximately like in LLVM host feature detection:
125     /// https://github.com/llvm-mirror/llvm/blob/master/lib/Support/Host.cpp#L1273
cache(self) -> cache::Initializer126     fn cache(self) -> cache::Initializer {
127         let mut value = cache::Initializer::default();
128         {
129             let mut enable_feature = |f, enable| {
130                 if enable {
131                     value.set(f as u32);
132                 }
133             };
134 
135             enable_feature(Feature::fp, self.fp);
136             // Half-float support requires float support
137             enable_feature(Feature::fp16, self.fp && self.fphp);
138             enable_feature(Feature::pmull, self.pmull);
139             enable_feature(Feature::crc, self.crc32);
140             enable_feature(Feature::lse, self.atomics);
141             enable_feature(Feature::rcpc, self.lrcpc);
142 
143             // SIMD support requires float support - if half-floats are
144             // supported, it also requires half-float support:
145             let asimd = self.fp && self.asimd && (!self.fphp | self.asimdhp);
146             enable_feature(Feature::asimd, asimd);
147             // SIMD extensions require SIMD support:
148             enable_feature(Feature::rdm, self.asimdrdm && asimd);
149             enable_feature(Feature::dotprod, self.asimddp && asimd);
150             enable_feature(Feature::sve, self.sve && asimd);
151 
152             // Crypto is specified as AES + PMULL + SHA1 + SHA2 per LLVM/hosts.cpp
153             enable_feature(
154                 Feature::crypto,
155                 self.aes && self.pmull && self.sha1 && self.sha2,
156             );
157         }
158         value
159     }
160 }
161