• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 <bitset>
31 #include <ostream>
32 
33 #include "globals-vixl.h"
34 
35 namespace vixl {
36 
37 
38 // VIXL aims to handle and detect all architectural features that are likely to
39 // influence code-generation decisions at EL0 (user-space).
40 //
41 // - There may be multiple VIXL feature flags for a given architectural
42 //   extension. This occurs where the extension allow components to be
43 //   implemented independently, or where kernel support is needed, and is likely
44 //   to be fragmented.
45 //
46 //   For example, Pointer Authentication (kPAuth*) has a separate feature flag
47 //   for access to PACGA, and to indicate that the QARMA algorithm is
48 //   implemented.
49 //
50 // - Conversely, some extensions have configuration options that do not affect
51 //   EL0, so these are presented as a single VIXL feature.
52 //
53 //   For example, the RAS extension (kRAS) has several variants, but the only
54 //   feature relevant to VIXL is the addition of the ESB instruction so we only
55 //   need a single flag.
56 //
57 // - VIXL offers separate flags for separate features even if they're
58 //   architecturally linked.
59 //
60 //   For example, the architecture requires kFPHalf and kNEONHalf to be equal,
61 //   but they have separate hardware ID register fields so VIXL presents them as
62 //   separate features.
63 //
64 // - VIXL can detect every feature for which it can generate code.
65 //
66 // - VIXL can detect some features for which it cannot generate code.
67 //
68 // The CPUFeatures::Feature enum — derived from the macro list below — is
69 // frequently extended. New features may be added to the list at any point, and
70 // no assumptions should be made about the numerical values assigned to each
71 // enum constant. The symbolic names can be considered to be stable.
72 //
73 // The debug descriptions are used only for debug output. The 'cpuinfo' strings
74 // are informative; VIXL does not use /proc/cpuinfo for feature detection.
75 
76 // clang-format off
77 #define VIXL_CPU_FEATURE_LIST(V)                                               \
78   /* If set, the OS traps and emulates MRS accesses to relevant (EL1) ID_*  */ \
79   /* registers, so that the detailed feature registers can be read          */ \
80   /* directly.                                                              */ \
81                                                                                \
82   /* Constant name        Debug description         Linux 'cpuinfo' string. */ \
83   V(kIDRegisterEmulation, "ID register emulation",  "cpuid")                   \
84                                                                                \
85   V(kFP,                  "FP",                     "fp")                      \
86   V(kNEON,                "NEON",                   "asimd")                   \
87   V(kCRC32,               "CRC32",                  "crc32")                   \
88   V(kDGH,                 "DGH",                    "dgh")                     \
89   /* Speculation control features.                                          */ \
90   V(kCSV2,                "CSV2",                   NULL)                      \
91   V(kSCXTNUM,             "SCXTNUM",                NULL)                      \
92   V(kCSV3,                "CSV3",                   NULL)                      \
93   V(kSB,                  "SB",                     "sb")                      \
94   V(kSPECRES,             "SPECRES",                NULL)                      \
95   V(kSSBS,                "SSBS",                   NULL)                      \
96   V(kSSBSControl,         "SSBS (PSTATE control)",  "ssbs")                    \
97   /* Cryptographic support instructions.                                    */ \
98   V(kAES,                 "AES",                    "aes")                     \
99   V(kSHA1,                "SHA1",                   "sha1")                    \
100   V(kSHA2,                "SHA2",                   "sha2")                    \
101   /* A form of PMULL{2} with a 128-bit (1Q) result.                         */ \
102   V(kPmull1Q,             "Pmull1Q",                "pmull")                   \
103   /* Atomic operations on memory: CAS, LDADD, STADD, SWP, etc.              */ \
104   V(kAtomics,             "Atomics",                "atomics")                 \
105   /* Limited ordering regions: LDLAR, STLLR and their variants.             */ \
106   V(kLORegions,           "LORegions",              NULL)                      \
107   /* Rounding doubling multiply add/subtract: SQRDMLAH and SQRDMLSH.        */ \
108   V(kRDM,                 "RDM",                    "asimdrdm")                \
109   /* Scalable Vector Extension.                                             */ \
110   V(kSVE,                 "SVE",                    "sve")                     \
111   V(kSVEF64MM,            "SVE F64MM",              "svef64mm")                \
112   V(kSVEF32MM,            "SVE F32MM",              "svef32mm")                \
113   V(kSVEI8MM,             "SVE I8MM",               "svei8imm")                \
114   V(kSVEBF16,             "SVE BFloat16",           "svebf16")                 \
115   /* SDOT and UDOT support (in NEON).                                       */ \
116   V(kDotProduct,          "DotProduct",             "asimddp")                 \
117   /* Int8 matrix multiplication (in NEON).                                  */ \
118   V(kI8MM,                "NEON I8MM",              "i8mm")                    \
119   /* Half-precision (FP16) support for FP and NEON, respectively.           */ \
120   V(kFPHalf,              "FPHalf",                 "fphp")                    \
121   V(kNEONHalf,            "NEONHalf",               "asimdhp")                 \
122   /* BFloat16 support (in both FP and NEON.)                                */ \
123   V(kBF16,                "FP/NEON BFloat 16",      "bf16")                    \
124   /* The RAS extension, including the ESB instruction.                      */ \
125   V(kRAS,                 "RAS",                    NULL)                      \
126   /* Data cache clean to the point of persistence: DC CVAP.                 */ \
127   V(kDCPoP,               "DCPoP",                  "dcpop")                   \
128   /* Data cache clean to the point of deep persistence: DC CVADP.           */ \
129   V(kDCCVADP,             "DCCVADP",                "dcpodp")                  \
130   /* Cryptographic support instructions.                                    */ \
131   V(kSHA3,                "SHA3",                   "sha3")                    \
132   V(kSHA512,              "SHA512",                 "sha512")                  \
133   V(kSM3,                 "SM3",                    "sm3")                     \
134   V(kSM4,                 "SM4",                    "sm4")                     \
135   /* Pointer authentication for addresses.                                  */ \
136   V(kPAuth,               "PAuth",                  "paca")                    \
137   /* Pointer authentication for addresses uses QARMA.                       */ \
138   V(kPAuthQARMA,          "PAuthQARMA",             NULL)                      \
139   /* Generic authentication (using the PACGA instruction).                  */ \
140   V(kPAuthGeneric,        "PAuthGeneric",           "pacg")                    \
141   /* Generic authentication uses QARMA.                                     */ \
142   V(kPAuthGenericQARMA,   "PAuthGenericQARMA",      NULL)                      \
143   /* JavaScript-style FP -> integer conversion instruction: FJCVTZS.        */ \
144   V(kJSCVT,               "JSCVT",                  "jscvt")                   \
145   /* Complex number support for NEON: FCMLA and FCADD.                      */ \
146   V(kFcma,                "Fcma",                   "fcma")                    \
147   /* RCpc-based model (for weaker release consistency): LDAPR and variants. */ \
148   V(kRCpc,                "RCpc",                   "lrcpc")                   \
149   V(kRCpcImm,             "RCpc (imm)",             "ilrcpc")                  \
150   /* Flag manipulation instructions: SETF{8,16}, CFINV, RMIF.               */ \
151   V(kFlagM,               "FlagM",                  "flagm")                   \
152   /* Unaligned single-copy atomicity.                                       */ \
153   V(kUSCAT,               "USCAT",                  "uscat")                   \
154   /* FP16 fused multiply-add or -subtract long: FMLAL{2}, FMLSL{2}.         */ \
155   V(kFHM,                 "FHM",                    "asimdfhm")                \
156   /* Data-independent timing (for selected instructions).                   */ \
157   V(kDIT,                 "DIT",                    "dit")                     \
158   /* Branch target identification.                                          */ \
159   V(kBTI,                 "BTI",                    "bti")                     \
160   /* Flag manipulation instructions: {AX,XA}FLAG                            */ \
161   V(kAXFlag,              "AXFlag",                 "flagm2")                  \
162   /* Random number generation extension,                                    */ \
163   V(kRNG,                 "RNG",                    "rng")                     \
164   /* Floating-point round to {32,64}-bit integer.                           */ \
165   V(kFrintToFixedSizedInt,"Frint (bounded)",        "frint")                   \
166   /* Memory Tagging Extension.                                              */ \
167   V(kMTEInstructions,     "MTE (EL0 instructions)", NULL)                      \
168   V(kMTE,                 "MTE",                    NULL)                      \
169   V(kMTE3,                "MTE (asymmetric)",       "mte3")                    \
170   /* PAuth extensions.                                                      */ \
171   V(kPAuthEnhancedPAC,    "PAuth EnhancedPAC",      NULL)                      \
172   V(kPAuthEnhancedPAC2,   "PAuth EnhancedPAC2",     NULL)                      \
173   V(kPAuthFPAC,           "PAuth FPAC",             NULL)                      \
174   V(kPAuthFPACCombined,   "PAuth FPACCombined",     NULL)                      \
175   /* Scalable Vector Extension 2.                                           */ \
176   V(kSVE2,                "SVE2",                   "sve2")                    \
177   V(kSVESM4,              "SVE SM4",                "svesm4")                  \
178   V(kSVESHA3,             "SVE SHA3",               "svesha3")                 \
179   V(kSVEBitPerm,          "SVE BitPerm",            "svebitperm")              \
180   V(kSVEAES,              "SVE AES",                "sveaes")                  \
181   V(kSVEPmull128,         "SVE Pmull128",           "svepmull")                \
182   /* Alternate floating-point behavior                                      */ \
183   V(kAFP,                 "AFP",                    "afp")                     \
184   /* Enhanced Counter Virtualization                                        */ \
185   V(kECV,                 "ECV",                    "ecv")                     \
186   /* Increased precision of Reciprocal Estimate and Square Root Estimate    */ \
187   V(kRPRES,               "RPRES",                  "rpres")                   \
188   /* Memory operation instructions, for memcpy, memset                      */ \
189   V(kMOPS,                "Memory ops",             NULL)                      \
190   /* Scalable Matrix Extension (SME)                                        */ \
191   V(kSME,                 "SME",                    "sme")                     \
192   V(kSMEi16i64,           "SME (i16i64)",           "smei16i64")               \
193   V(kSMEf64f64,           "SME (f64f64)",           "smef64f64")               \
194   V(kSMEi8i32,            "SME (i8i32)",            "smei8i32")                \
195   V(kSMEf16f32,           "SME (f16f32)",           "smef16f32")               \
196   V(kSMEb16f32,           "SME (b16f32)",           "smeb16f32")               \
197   V(kSMEf32f32,           "SME (f32f32)",           "smef32f32")               \
198   V(kSMEfa64,             "SME (fa64)",             "smefa64")                 \
199   /* WFET and WFIT instruction support                                      */ \
200   V(kWFXT,                "WFXT",                   "wfxt")                    \
201   /* Extended BFloat16 instructions                                         */ \
202   V(kEBF16,               "EBF16",                  "ebf16")                   \
203   V(kSVE_EBF16,           "EBF16 (SVE)",            "sveebf16")                \
204   V(kCSSC,                "CSSC",                   "cssc")                    \
205   V(kGCS,                 "GCS",                    "gcs")
206 // clang-format on
207 
208 
209 class CPUFeaturesConstIterator;
210 
211 // A representation of the set of features known to be supported by the target
212 // device. Each feature is represented by a simple boolean flag.
213 //
214 //   - When the Assembler is asked to assemble an instruction, it asserts (in
215 //     debug mode) that the necessary features are available.
216 //
217 //   - TODO: The MacroAssembler relies on the Assembler's assertions, but in
218 //     some cases it may be useful for macros to generate a fall-back sequence
219 //     in case features are not available.
220 //
221 //   - The Simulator assumes by default that all features are available, but it
222 //     is possible to configure it to fail if the simulated code uses features
223 //     that are not enabled.
224 //
225 //     The Simulator also offers pseudo-instructions to allow features to be
226 //     enabled and disabled dynamically. This is useful when you want to ensure
227 //     that some features are constrained to certain areas of code.
228 //
229 //   - The base Disassembler knows nothing about CPU features, but the
230 //     PrintDisassembler can be configured to annotate its output with warnings
231 //     about unavailable features. The Simulator uses this feature when
232 //     instruction trace is enabled.
233 //
234 //   - The Decoder-based components -- the Simulator and PrintDisassembler --
235 //     rely on a CPUFeaturesAuditor visitor. This visitor keeps a list of
236 //     features actually encountered so that a large block of code can be
237 //     examined (either directly or through simulation), and the required
238 //     features analysed later.
239 //
240 // Expected usage:
241 //
242 //     // By default, VIXL uses CPUFeatures::AArch64LegacyBaseline(), for
243 //     // compatibility with older version of VIXL.
244 //     MacroAssembler masm;
245 //
246 //     // Generate code only for the current CPU.
247 //     masm.SetCPUFeatures(CPUFeatures::InferFromOS());
248 //
249 //     // Turn off feature checking entirely.
250 //     masm.SetCPUFeatures(CPUFeatures::All());
251 //
252 // Feature set manipulation:
253 //
254 //     CPUFeatures f;  // The default constructor gives an empty set.
255 //     // Individual features can be added (or removed).
256 //     f.Combine(CPUFeatures::kFP, CPUFeatures::kNEON, CPUFeatures::AES);
257 //     f.Remove(CPUFeatures::kNEON);
258 //
259 //     // Some helpers exist for extensions that provide several features.
260 //     f.Remove(CPUFeatures::All());
261 //     f.Combine(CPUFeatures::AArch64LegacyBaseline());
262 //
263 //     // Chained construction is also possible.
264 //     CPUFeatures g =
265 //         f.With(CPUFeatures::kPmull1Q).Without(CPUFeatures::kCRC32);
266 //
267 //     // Features can be queried. Where multiple features are given, they are
268 //     // combined with logical AND.
269 //     if (h.Has(CPUFeatures::kNEON)) { ... }
270 //     if (h.Has(CPUFeatures::kFP, CPUFeatures::kNEON)) { ... }
271 //     if (h.Has(g)) { ... }
272 //     // If the empty set is requested, the result is always 'true'.
273 //     VIXL_ASSERT(h.Has(CPUFeatures()));
274 //
275 //     // For debug and reporting purposes, features can be enumerated (or
276 //     // printed directly):
277 //     std::cout << CPUFeatures::kNEON;  // Prints something like "NEON".
278 //     std::cout << f;  // Prints something like "FP, NEON, CRC32".
279 class CPUFeatures {
280  public:
281   // clang-format off
282   // Individual features.
283   // These should be treated as opaque tokens. User code should not rely on
284   // specific numeric values or ordering.
285   enum Feature {
286     // Refer to VIXL_CPU_FEATURE_LIST (above) for the list of feature names that
287     // this class supports.
288 
289     kNone = -1,
290 #define VIXL_DECLARE_FEATURE(SYMBOL, NAME, CPUINFO) SYMBOL,
291     VIXL_CPU_FEATURE_LIST(VIXL_DECLARE_FEATURE)
292 #undef VIXL_DECLARE_FEATURE
293     kNumberOfFeatures
294   };
295   // clang-format on
296 
297   // By default, construct with no features enabled.
CPUFeatures()298   CPUFeatures() : features_{} {}
299 
300   // Construct with some features already enabled.
301   template <typename T, typename... U>
CPUFeatures(T first,U...others)302   CPUFeatures(T first, U... others) : features_{} {
303     Combine(first, others...);
304   }
305 
306   // Construct with all features enabled. This can be used to disable feature
307   // checking: `Has(...)` returns true regardless of the argument.
308   static CPUFeatures All();
309 
310   // Construct an empty CPUFeatures. This is equivalent to the default
311   // constructor, but is provided for symmetry and convenience.
None()312   static CPUFeatures None() { return CPUFeatures(); }
313 
314   // The presence of these features was assumed by version of VIXL before this
315   // API was added, so using this set by default ensures API compatibility.
AArch64LegacyBaseline()316   static CPUFeatures AArch64LegacyBaseline() {
317     return CPUFeatures(kFP, kNEON, kCRC32);
318   }
319 
320   // Construct a new CPUFeatures object using ID registers. This assumes that
321   // kIDRegisterEmulation is present.
322   static CPUFeatures InferFromIDRegisters();
323 
324   enum QueryIDRegistersOption {
325     kDontQueryIDRegisters,
326     kQueryIDRegistersIfAvailable
327   };
328 
329   // Construct a new CPUFeatures object based on what the OS reports.
330   static CPUFeatures InferFromOS(
331       QueryIDRegistersOption option = kQueryIDRegistersIfAvailable);
332 
333   // Combine another CPUFeatures object into this one. Features that already
334   // exist in this set are left unchanged.
335   void Combine(const CPUFeatures& other);
336 
337   // Combine a specific feature into this set. If it already exists in the set,
338   // the set is left unchanged.
339   void Combine(Feature feature);
340 
341   // Combine multiple features (or feature sets) into this set.
342   template <typename T, typename... U>
Combine(T first,U...others)343   void Combine(T first, U... others) {
344     Combine(first);
345     Combine(others...);
346   }
347 
348   // Remove features in another CPUFeatures object from this one.
349   void Remove(const CPUFeatures& other);
350 
351   // Remove a specific feature from this set. This has no effect if the feature
352   // doesn't exist in the set.
353   void Remove(Feature feature0);
354 
355   // Remove multiple features (or feature sets) from this set.
356   template <typename T, typename... U>
Remove(T first,U...others)357   void Remove(T first, U... others) {
358     Remove(first);
359     Remove(others...);
360   }
361 
362   // Chaining helpers for convenient construction by combining other CPUFeatures
363   // or individual Features.
364   template <typename... T>
With(T...others)365   CPUFeatures With(T... others) const {
366     CPUFeatures f(*this);
367     f.Combine(others...);
368     return f;
369   }
370 
371   template <typename... T>
Without(T...others)372   CPUFeatures Without(T... others) const {
373     CPUFeatures f(*this);
374     f.Remove(others...);
375     return f;
376   }
377 
378   // Test whether the `other` feature set is equal to or a subset of this one.
379   bool Has(const CPUFeatures& other) const;
380 
381   // Test whether a single feature exists in this set.
382   // Note that `Has(kNone)` always returns true.
383   bool Has(Feature feature) const;
384 
385   // Test whether all of the specified features exist in this set.
386   template <typename T, typename... U>
Has(T first,U...others)387   bool Has(T first, U... others) const {
388     return Has(first) && Has(others...);
389   }
390 
391   // Return the number of enabled features.
392   size_t Count() const;
HasNoFeatures()393   bool HasNoFeatures() const { return Count() == 0; }
394 
395   // Check for equivalence.
396   bool operator==(const CPUFeatures& other) const {
397     return Has(other) && other.Has(*this);
398   }
399   bool operator!=(const CPUFeatures& other) const { return !(*this == other); }
400 
401   typedef CPUFeaturesConstIterator const_iterator;
402 
403   const_iterator begin() const;
404   const_iterator end() const;
405 
406  private:
407   // Each bit represents a feature. This set will be extended as needed.
408   std::bitset<kNumberOfFeatures> features_;
409 
410   friend std::ostream& operator<<(std::ostream& os,
411                                   const vixl::CPUFeatures& features);
412 };
413 
414 std::ostream& operator<<(std::ostream& os, vixl::CPUFeatures::Feature feature);
415 std::ostream& operator<<(std::ostream& os, const vixl::CPUFeatures& features);
416 
417 // This is not a proper C++ iterator type, but it simulates enough of
418 // ForwardIterator that simple loops can be written.
419 class CPUFeaturesConstIterator {
420  public:
421   CPUFeaturesConstIterator(const CPUFeatures* cpu_features = NULL,
422                            CPUFeatures::Feature start = CPUFeatures::kNone)
cpu_features_(cpu_features)423       : cpu_features_(cpu_features), feature_(start) {
424     VIXL_ASSERT(IsValid());
425   }
426 
427   bool operator==(const CPUFeaturesConstIterator& other) const;
428   bool operator!=(const CPUFeaturesConstIterator& other) const {
429     return !(*this == other);
430   }
431   CPUFeaturesConstIterator& operator++();
432   CPUFeaturesConstIterator operator++(int);
433 
434   CPUFeatures::Feature operator*() const {
435     VIXL_ASSERT(IsValid());
436     return feature_;
437   }
438 
439   // For proper support of C++'s simplest "Iterator" concept, this class would
440   // have to define member types (such as CPUFeaturesIterator::pointer) to make
441   // it appear as if it iterates over Feature objects in memory. That is, we'd
442   // need CPUFeatures::iterator to behave like std::vector<Feature>::iterator.
443   // This is at least partially possible -- the std::vector<bool> specialisation
444   // does something similar -- but it doesn't seem worthwhile for a
445   // special-purpose debug helper, so they are omitted here.
446  private:
447   const CPUFeatures* cpu_features_;
448   CPUFeatures::Feature feature_;
449 
IsValid()450   bool IsValid() const {
451     if (cpu_features_ == NULL) {
452       return feature_ == CPUFeatures::kNone;
453     }
454     return cpu_features_->Has(feature_);
455   }
456 };
457 
458 // A convenience scope for temporarily modifying a CPU features object. This
459 // allows features to be enabled for short sequences.
460 //
461 // Expected usage:
462 //
463 //  {
464 //    CPUFeaturesScope cpu(&masm, CPUFeatures::kCRC32);
465 //    // This scope can now use CRC32, as well as anything else that was enabled
466 //    // before the scope.
467 //
468 //    ...
469 //
470 //    // At the end of the scope, the original CPU features are restored.
471 //  }
472 class CPUFeaturesScope {
473  public:
474   // Start a CPUFeaturesScope on any object that implements
475   // `CPUFeatures* GetCPUFeatures()`.
476   template <typename T>
CPUFeaturesScope(T * cpu_features_wrapper)477   explicit CPUFeaturesScope(T* cpu_features_wrapper)
478       : cpu_features_(cpu_features_wrapper->GetCPUFeatures()),
479         old_features_(*cpu_features_) {}
480 
481   // Start a CPUFeaturesScope on any object that implements
482   // `CPUFeatures* GetCPUFeatures()`, with the specified features enabled.
483   template <typename T, typename U, typename... V>
CPUFeaturesScope(T * cpu_features_wrapper,U first,V...features)484   CPUFeaturesScope(T* cpu_features_wrapper, U first, V... features)
485       : cpu_features_(cpu_features_wrapper->GetCPUFeatures()),
486         old_features_(*cpu_features_) {
487     cpu_features_->Combine(first, features...);
488   }
489 
~CPUFeaturesScope()490   ~CPUFeaturesScope() { *cpu_features_ = old_features_; }
491 
492   // For advanced usage, the CPUFeatures object can be accessed directly.
493   // The scope will restore the original state when it ends.
494 
GetCPUFeatures()495   CPUFeatures* GetCPUFeatures() const { return cpu_features_; }
496 
SetCPUFeatures(const CPUFeatures & cpu_features)497   void SetCPUFeatures(const CPUFeatures& cpu_features) {
498     *cpu_features_ = cpu_features;
499   }
500 
501  private:
502   CPUFeatures* const cpu_features_;
503   const CPUFeatures old_features_;
504 };
505 
506 
507 }  // namespace vixl
508 
509 #endif  // VIXL_CPU_FEATURES_H
510