blob: a0475cc21448925d129a37857aaca627d8cdd1f6 [file] [log] [blame]
Jacob Bramley2af191d2018-05-16 10:22:44 +01001// Copyright 2018, VIXL authors
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7// * Redistributions of source code must retain the above copyright notice,
8// this list of conditions and the following disclaimer.
9// * Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12// * Neither the name of ARM Limited nor the names of its contributors may be
13// used to endorse or promote products derived from this software without
14// specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
17// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27#ifndef VIXL_CPU_FEATURES_H
28#define VIXL_CPU_FEATURES_H
29
30#include <ostream>
31
32#include "globals-vixl.h"
33
34namespace vixl {
35
36
37// clang-format off
38#define VIXL_CPU_FEATURE_LIST(V) \
39 /* If set, the OS traps and emulates MRS accesses to relevant (EL1) ID_* */ \
40 /* registers, so that the detailed feature registers can be read */ \
41 /* directly. */ \
42 V(kIDRegisterEmulation, "ID register emulation", "cpuid") \
43 \
44 V(kFP, "FP", "fp") \
45 V(kNEON, "NEON", "asimd") \
46 V(kCRC32, "CRC32", "crc32") \
47 /* Cryptographic support instructions. */ \
48 V(kAES, "AES", "aes") \
49 V(kSHA1, "SHA1", "sha1") \
50 V(kSHA2, "SHA2", "sha2") \
51 /* A form of PMULL{2} with a 128-bit (1Q) result. */ \
52 V(kPmull1Q, "Pmull1Q", "pmull") \
53 /* Atomic operations on memory: CAS, LDADD, STADD, SWP, etc. */ \
54 V(kAtomics, "Atomics", "atomics") \
55 /* Limited ordering regions: LDLAR, STLLR and their variants. */ \
56 V(kLORegions, "LORegions", NULL) \
57 /* Rounding doubling multiply add/subtract: SQRDMLAH and SQRDMLSH. */ \
58 V(kRDM, "RDM", "asimdrdm") \
59 /* SDOT and UDOT support (in NEON). */ \
60 V(kDotProduct, "DotProduct", "asimddp") \
61 /* Half-precision (FP16) support for FP and NEON, respectively. */ \
62 V(kFPHalf, "FPHalf", "fphp") \
63 V(kNEONHalf, "NEONHalf", "asimdhp") \
64 /* Data cache clean to the point of persistence: DC CVAP. */ \
65 V(kDCPoP, "DCPoP", "dcpop") \
66 /* Cryptographic support instructions. */ \
67 V(kSHA3, "SHA3", "sha3") \
68 V(kSHA512, "SHA512", "sha512") \
69 V(kSM3, "SM3", "sm3") \
70 V(kSM4, "SM4", "sm4") \
71 /* Pointer authentication for addresses. */ \
72 V(kPAuth, "PAuth", NULL) \
73 /* Pointer authentication for addresses uses QARMA. */ \
74 V(kPAuthQARMA, "PAuthQARMA", NULL) \
75 /* Generic authentication (using the PACGA instruction). */ \
76 V(kPAuthGeneric, "PAuthGeneric", NULL) \
77 /* Generic authentication uses QARMA. */ \
78 V(kPAuthGenericQARMA, "PAuthGenericQARMA", NULL) \
79 /* JavaScript-style FP <-> integer conversion instruction: FJCVTZS. */ \
80 V(kJSCVT, "JSCVT", "jscvt") \
81 /* RCpc-based model (for weaker release consistency): LDAPR and variants. */ \
82 V(kRCpc, "RCpc", "lrcpc") \
83 /* Complex number support for NEON: FCMLA and FCADD. */ \
84 V(kFcma, "Fcma", "fcma")
85// clang-format on
86
87
88class CPUFeaturesConstIterator;
89
90// A representation of the set of features known to be supported by the target
91// device. Each feature is represented by a simple boolean flag.
92//
Jacob Bramley5997b462018-06-05 14:05:30 +010093// - When the Assembler is asked to assemble an instruction, it asserts (in
94// debug mode) that the necessary features are available.
Jacob Bramley2af191d2018-05-16 10:22:44 +010095//
96// - TODO: The Simulator assumes that all features are available by default,
97// but it should be possible to configure it to either warn or fail if the
98// simulated code uses features that are not enabled.
99//
100// - TODO: The Disassembler disassembles all instructions that it knows, but
101// it could be (optionally) configured to warn if the code uses features or
102// instructions that are not available.
103//
104// - TODO: The MacroAssembler relies on the Assembler's assertions, but in
105// some cases it may be useful for macros to generate a fall-back sequence
106// in case features are not available.
107//
108// Expected usage:
109//
110// // By default, VIXL uses CPUFeatures::AArch64LegacyBaseline(), for
111// // compatibility with older version of VIXL.
112// MacroAssembler masm;
113//
114// // Generate code only for the current CPU.
115// masm.SetCPUFeatures(CPUFeatures::InferFromOS());
116//
117// // Turn off feature checking entirely.
118// masm.SetCPUFeatures(CPUFeatures::All());
119//
120// Feature set manipulation:
121//
122// CPUFeatures f; // The default constructor gives an empty set.
123// // Individual features can be added (or removed).
124// f.Combine(CPUFeatures::kFP, CPUFeatures::kNEON, CPUFeatures::AES);
125// f.Remove(CPUFeatures::kNEON);
126//
127// // Some helpers exist for extensions that provide several features.
128// f.Remove(CPUFeatures::All());
129// f.Combine(CPUFeatures::AArch64LegacyBaseline());
130//
131// // Chained construction is also possible.
132// CPUFeatures g =
133// f.With(CPUFeatures::kPmull1Q).Without(CPUFeatures::kCRC32);
134//
135// // Features can be queried. Where multiple features are given, they are
136// // combined with logical AND.
137// if (h.Has(CPUFeatures::kNEON)) { ... }
138// if (h.Has(CPUFeatures::kFP, CPUFeatures::kNEON)) { ... }
139// if (h.Has(g)) { ... }
140// // If the empty set is requested, the result is always 'true'.
141// VIXL_ASSERT(h.Has(CPUFeatures()));
142//
143// // For debug and reporting purposes, features can be enumerated (or
144// // printed directly):
145// std::cout << CPUFeatures::kNEON; // Prints something like "NEON".
146// std::cout << f; // Prints something like "FP, NEON, CRC32".
147class CPUFeatures {
148 public:
149 // clang-format off
150 // Individual features.
151 // These should be treated as opaque tokens. User code should not rely on
152 // specific numeric values or ordering.
153 enum Feature {
154 // Refer to VIXL_CPU_FEATURE_LIST (above) for the list of feature names that
155 // this class supports.
156
157#define VIXL_DECLARE_FEATURE(SYMBOL, NAME, CPUINFO) SYMBOL,
158 VIXL_CPU_FEATURE_LIST(VIXL_DECLARE_FEATURE)
159#undef VIXL_DECLARE_FEATURE
160 kNone,
161 kNumberOfFeatures = kNone
162 };
163 // clang-format on
164
165 // By default, construct with no features enabled.
166 CPUFeatures() : features_(0) {}
167
168 // Construct with some features already enabled.
169 CPUFeatures(Feature feature0,
170 Feature feature1 = kNone,
171 Feature feature2 = kNone,
172 Feature feature3 = kNone);
173
174 // Construct with all features enabled. This can be used to disable feature
175 // checking: `Has(...)` returns true regardless of the argument.
176 static CPUFeatures All();
177
178 // The presence of these features was assumed by version of VIXL before this
179 // API was added, so using this set by default ensures API compatibility.
180 static CPUFeatures AArch64LegacyBaseline() {
181 return CPUFeatures(kFP, kNEON, kCRC32);
182 }
183
184 // Construct a new CPUFeatures object based on what the OS reports.
185 static CPUFeatures InferFromOS();
186
187 // Combine another CPUFeatures object into this one. Features that already
188 // exist in this set are left unchanged.
189 void Combine(const CPUFeatures& other);
190
191 // Combine specific features into this set. Features that already exist in
192 // this set are left unchanged.
193 void Combine(Feature feature0,
194 Feature feature1 = kNone,
195 Feature feature2 = kNone,
196 Feature feature3 = kNone);
197
198 // Remove features in another CPUFeatures object from this one.
199 void Remove(const CPUFeatures& other);
200
201 // Remove specific features from this set.
202 void Remove(Feature feature0,
203 Feature feature1 = kNone,
204 Feature feature2 = kNone,
205 Feature feature3 = kNone);
206
207 // Chaining helpers for convenient construction.
208 CPUFeatures With(const CPUFeatures& other) const;
209 CPUFeatures With(Feature feature0,
210 Feature feature1 = kNone,
211 Feature feature2 = kNone,
212 Feature feature3 = kNone) const;
213 CPUFeatures Without(const CPUFeatures& other) const;
214 CPUFeatures Without(Feature feature0,
215 Feature feature1 = kNone,
216 Feature feature2 = kNone,
217 Feature feature3 = kNone) const;
218
219 // Query features.
220 // Note that an empty query (like `Has(kNone)`) always returns true.
221 bool Has(const CPUFeatures& other) const;
222 bool Has(Feature feature0,
223 Feature feature1 = kNone,
224 Feature feature2 = kNone,
225 Feature feature3 = kNone) const;
226
227 typedef CPUFeaturesConstIterator const_iterator;
228
229 const_iterator begin() const;
230 const_iterator end() const;
231
232 private:
233 // Each bit represents a feature. This field will be replaced as needed if
234 // features are added.
235 uint64_t features_;
236
237 friend std::ostream& operator<<(std::ostream& os,
238 const vixl::CPUFeatures& features);
239};
240
241std::ostream& operator<<(std::ostream& os, vixl::CPUFeatures::Feature feature);
242std::ostream& operator<<(std::ostream& os, const vixl::CPUFeatures& features);
243
244// This is not a proper C++ iterator type, but it simulates enough of
245// ForwardIterator that simple loops can be written.
246class CPUFeaturesConstIterator {
247 public:
248 CPUFeaturesConstIterator(const CPUFeatures* cpu_features = NULL,
249 CPUFeatures::Feature start = CPUFeatures::kNone)
250 : cpu_features_(cpu_features), feature_(start) {
251 VIXL_ASSERT(IsValid());
252 }
253
254 bool operator==(const CPUFeaturesConstIterator& other) const;
255 bool operator!=(const CPUFeaturesConstIterator& other) const {
256 return !(*this == other);
257 }
258 CPUFeatures::Feature operator++();
259 CPUFeatures::Feature operator++(int);
260
261 CPUFeatures::Feature operator*() const {
262 VIXL_ASSERT(IsValid());
263 return feature_;
264 }
265
266 // For proper support of C++'s simplest "Iterator" concept, this class would
267 // have to define member types (such as CPUFeaturesIterator::pointer) to make
268 // it appear as if it iterates over Feature objects in memory. That is, we'd
269 // need CPUFeatures::iterator to behave like std::vector<Feature>::iterator.
270 // This is at least partially possible -- the std::vector<bool> specialisation
271 // does something similar -- but it doesn't seem worthwhile for a
272 // special-purpose debug helper, so they are omitted here.
273 private:
274 const CPUFeatures* cpu_features_;
275 CPUFeatures::Feature feature_;
276
277 bool IsValid() const {
278 return ((cpu_features_ == NULL) && (feature_ == CPUFeatures::kNone)) ||
279 cpu_features_->Has(feature_);
280 }
281};
282
283// A convenience scope for temporarily modifying a CPU features object. This
284// allows features to be enabled for short sequences.
285//
286// Expected usage:
287//
288// {
289// CPUFeaturesScope cpu(&masm, CPUFeatures::kCRC32);
290// // This scope can now use CRC32, as well as anything else that was enabled
291// // before the scope.
292//
293// ...
294//
295// // At the end of the scope, the original CPU features are restored.
296// }
297class CPUFeaturesScope {
298 public:
299 // Start a CPUFeaturesScope on any object that implements
300 // `CPUFeatures* GetCPUFeatures()`.
301 template <typename T>
302 explicit CPUFeaturesScope(T* cpu_features_wrapper,
303 CPUFeatures::Feature feature0 = CPUFeatures::kNone,
304 CPUFeatures::Feature feature1 = CPUFeatures::kNone,
305 CPUFeatures::Feature feature2 = CPUFeatures::kNone,
306 CPUFeatures::Feature feature3 = CPUFeatures::kNone)
307 : cpu_features_(cpu_features_wrapper->GetCPUFeatures()),
308 old_features_(*cpu_features_) {
309 cpu_features_->Combine(feature0, feature1, feature2, feature3);
310 }
311
312 template <typename T>
313 CPUFeaturesScope(T* cpu_features_wrapper, const CPUFeatures& other)
314 : cpu_features_(cpu_features_wrapper->GetCPUFeatures()),
315 old_features_(*cpu_features_) {
316 cpu_features_->Combine(other);
317 }
318
319 ~CPUFeaturesScope() { *cpu_features_ = old_features_; }
320
321 // For advanced usage, the CPUFeatures object can be accessed directly.
322 // The scope will restore the original state when it ends.
323
324 CPUFeatures* GetCPUFeatures() const { return cpu_features_; }
325
326 void SetCPUFeatures(const CPUFeatures& cpu_features) {
327 *cpu_features_ = cpu_features;
328 }
329
330 private:
331 CPUFeatures* const cpu_features_;
332 const CPUFeatures old_features_;
333};
334
335
336} // namespace vixl
337
338#endif // VIXL_CPU_FEATURES_H