1 // Copyright 2016, 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 #include <iostream>
28 #include <set>
29 #include <sstream>
30 #include <vector>
31 
32 #include "test-runner.h"
33 
34 #include "cpu-features.h"
35 #include "utils-vixl.h"
36 
37 #if __cplusplus >= 201103L
38 #include <type_traits>
39 #endif
40 
41 #define TEST(name) TEST_(API_##name)
42 
43 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
44 
45 namespace vixl {
46 
47 // Describe the result of a test. Should IsUintN() and IsIntN() return true or
48 // false for N and X?
49 template <typename T>
50 struct UintIntTest {
51   bool is_uintn;
52   bool is_intn;
53   unsigned n;
54   T x;
55 };
56 
57 // Test IsUintN() and IsIntN() against various values and integral types.
TEST(IsUint_IsInt)58 TEST(IsUint_IsInt) {
59   UintIntTest<uint32_t> test_little_values_unsigned[] = {
60       {true, true, 1, UINT32_C(0x0)},   {true, false, 1, UINT32_C(0x1)},
61       {false, false, 1, UINT32_C(0x2)}, {false, false, 1, UINT32_C(0x3)},
62       {false, false, 1, UINT32_C(0x4)}, {false, false, 1, UINT32_C(0x5)},
63       {false, false, 1, UINT32_C(0x6)}, {false, false, 1, UINT32_C(0x7)},
64       {false, false, 1, UINT32_C(0x8)}, {false, false, 1, UINT32_C(0x9)},
65       {false, false, 1, UINT32_C(0xa)}, {false, false, 1, UINT32_C(0xb)},
66       {false, false, 1, UINT32_C(0xc)}, {false, false, 1, UINT32_C(0xd)},
67       {false, false, 1, UINT32_C(0xe)}, {false, false, 1, UINT32_C(0xf)},
68 
69       {true, true, 2, UINT32_C(0x0)},   {true, true, 2, UINT32_C(0x1)},
70       {true, false, 2, UINT32_C(0x2)},  {true, false, 2, UINT32_C(0x3)},
71       {false, false, 2, UINT32_C(0x4)}, {false, false, 2, UINT32_C(0x5)},
72       {false, false, 2, UINT32_C(0x6)}, {false, false, 2, UINT32_C(0x7)},
73       {false, false, 2, UINT32_C(0x8)}, {false, false, 2, UINT32_C(0x9)},
74       {false, false, 2, UINT32_C(0xa)}, {false, false, 2, UINT32_C(0xb)},
75       {false, false, 2, UINT32_C(0xc)}, {false, false, 2, UINT32_C(0xd)},
76       {false, false, 2, UINT32_C(0xe)}, {false, false, 2, UINT32_C(0xf)},
77   };
78 
79   UintIntTest<int32_t> test_little_values_signed[] = {
80       {true, true, 1, INT32_C(0)},    {true, false, 1, INT32_C(1)},
81       {false, false, 1, INT32_C(2)},  {false, false, 1, INT32_C(3)},
82       {false, false, 1, INT32_C(4)},  {false, false, 1, INT32_C(5)},
83       {false, false, 1, INT32_C(6)},  {false, false, 1, INT32_C(7)},
84       {false, true, 1, INT32_C(-1)},  {false, false, 1, INT32_C(-2)},
85       {false, false, 1, INT32_C(-3)}, {false, false, 1, INT32_C(-4)},
86       {false, false, 1, INT32_C(-5)}, {false, false, 1, INT32_C(-6)},
87       {false, false, 1, INT32_C(-7)}, {false, false, 1, INT32_C(-8)},
88 
89       {true, true, 2, INT32_C(0)},    {true, true, 2, INT32_C(1)},
90       {true, false, 2, INT32_C(2)},   {true, false, 2, INT32_C(3)},
91       {false, false, 2, INT32_C(4)},  {false, false, 2, INT32_C(5)},
92       {false, false, 2, INT32_C(6)},  {false, false, 2, INT32_C(7)},
93       {false, true, 2, INT32_C(-1)},  {false, true, 2, INT32_C(-2)},
94       {false, false, 2, INT32_C(-3)}, {false, false, 2, INT32_C(-4)},
95       {false, false, 2, INT32_C(-5)}, {false, false, 2, INT32_C(-6)},
96       {false, false, 2, INT32_C(-7)}, {false, false, 2, INT32_C(-8)},
97   };
98 
99   UintIntTest<uint32_t> test_u16[] = {
100       {true, true, 16, UINT32_C(0x0)},
101       {true, false, 16, UINT32_C(0xabcd)},
102       {true, false, 16, UINT32_C(0x8000)},
103       {true, false, 16, UINT32_C(0xffff)},
104       {false, false, 16, UINT32_C(0x10000)},
105       {false, false, 16, UINT32_C(0xffff0000)},
106       {false, false, 16, UINT32_C(0xffff8000)},
107       {false, false, 16, UINT32_C(0xffffffff)},
108   };
109 
110   UintIntTest<int32_t> test_i16[] = {
111       {true, true, 16, INT32_C(0x0)},
112       {true, false, 16, INT32_C(0xabcd)},
113       {true, false, 16, INT32_C(0x8000)},
114       {true, false, 16, INT32_C(0xffff)},
115       {false, false, 16, INT32_C(0x10000)},
116       {true, true, 16, INT32_C(42)},
117       {false, true, 16, INT32_C(-42)},
118       {false, true, 16, INT32_C(-1)},
119   };
120 
121   UintIntTest<uint64_t> test_u32[] = {
122       {true, true, 32, UINT64_C(0x0)},
123       {true, false, 32, UINT64_C(0xabcdabcd)},
124       {true, false, 32, UINT64_C(0x80000000)},
125       {true, false, 32, UINT64_C(0xffffffff)},
126   };
127 
128   UintIntTest<int64_t> test_i32[] = {
129       {true, true, 32, INT64_C(0)},
130       {true, true, 32, INT64_C(42)},
131       {false, true, 32, INT64_C(-42)},
132       {false, true, 32, INT64_C(-1)},
133       {true, true, 32, INT64_C(2147483647)},    // (1 << (32 - 1)) - 1
134       {false, true, 32, INT64_C(-2147483648)},  // -(1 << (32 - 1))
135   };
136 
137   UintIntTest<uint64_t> test_unsigned_higher_than_32[] = {
138       {false, false, 54, UINT64_C(0xabcdef9012345678)},
139       {true, false, 33, UINT64_C(0x100000000)},
140       {true, false, 62, UINT64_C(0x3fffffffffffffff)},
141       {true, false, 63, UINT64_C(0x7fffffffffffffff)},
142   };
143 
144   UintIntTest<int64_t> test_signed_higher_than_32[] = {
145       {true, true, 54, INT64_C(9007199254740991)},   // (1 << (54 - 1)) - 1
146       {true, false, 54, INT64_C(9007199254740992)},  // 1 << (54 - 1)
147       {true, true, 33, INT64_C(4294967295)},         // (1 << (33 - 1) - 1)
148       {false, true, 33, INT64_C(-4294967296)},       // -(1 << (33 - 1))
149   };
150 
151 #define TEST_LIST(M)              \
152   M(test_little_values_unsigned)  \
153   M(test_little_values_signed)    \
154   M(test_u16)                     \
155   M(test_i16)                     \
156   M(test_u32)                     \
157   M(test_i32)                     \
158   M(test_unsigned_higher_than_32) \
159   M(test_signed_higher_than_32)
160 
161 
162 #define TEST_UINT(test_vector)                                  \
163   for (unsigned i = 0; i < ARRAY_SIZE(test_vector); i++) {      \
164     if (test_vector[i].is_uintn) {                              \
165       VIXL_CHECK(IsUintN(test_vector[i].n, test_vector[i].x));  \
166     } else {                                                    \
167       VIXL_CHECK(!IsUintN(test_vector[i].n, test_vector[i].x)); \
168     }                                                           \
169   }
170 
171 #define TEST_INT(test_vector)                                  \
172   for (unsigned i = 0; i < ARRAY_SIZE(test_vector); i++) {     \
173     if (test_vector[i].is_intn) {                              \
174       VIXL_CHECK(IsIntN(test_vector[i].n, test_vector[i].x));  \
175     } else {                                                   \
176       VIXL_CHECK(!IsIntN(test_vector[i].n, test_vector[i].x)); \
177     }                                                          \
178   }
179 
180   TEST_LIST(TEST_UINT)
181   TEST_LIST(TEST_INT)
182 
183 #undef TEST_UINT
184 #undef TEST_INT
185 
186 #undef TEST_LIST
187 }
188 
189 
TEST(CPUFeatures_iterator_api)190 TEST(CPUFeatures_iterator_api) {
191   // CPUFeaturesIterator does not fully satisfy the requirements of C++'s
192   // iterator concepts, but it should implement enough for some basic usage.
193 
194   // Arbitrary feature lists.
195   CPUFeatures f1(CPUFeatures::kFP, CPUFeatures::kNEON);
196   CPUFeatures f2(CPUFeatures::kFP, CPUFeatures::kNEON, CPUFeatures::kCRC32);
197   CPUFeatures f3;
198 
199   typedef CPUFeatures::const_iterator It;
200 
201   It it0;
202   It it1_neon(&f1, CPUFeatures::kNEON);
203   It it2_neon(&f2, CPUFeatures::kNEON);
204   It it2_crc32(&f2, CPUFeatures::kCRC32);
205   It it3(&f3);
206 
207   // Equality
208   VIXL_CHECK(it0 == it0);
209   VIXL_CHECK(it1_neon == it1_neon);
210   VIXL_CHECK(it2_neon == it2_neon);
211   VIXL_CHECK(it2_crc32 == it2_crc32);
212   VIXL_CHECK(it3 == it3);
213   VIXL_CHECK(!(it0 == it1_neon));
214   VIXL_CHECK(!(it0 == it3));
215   VIXL_CHECK(!(it1_neon == it2_neon));
216   VIXL_CHECK(!(it1_neon == it2_crc32));
217   VIXL_CHECK(!(it1_neon == it3));
218   VIXL_CHECK(!(it2_neon == it2_crc32));
219   VIXL_CHECK(!(it3 == it0));
220   VIXL_CHECK(!(it3 == it1_neon));
221 
222   // Inequality
223   //   (a == b)  <->  !(a != b)
224   VIXL_CHECK(!(it0 != it0));
225   VIXL_CHECK(!(it1_neon != it1_neon));
226   VIXL_CHECK(!(it2_neon != it2_neon));
227   VIXL_CHECK(!(it2_crc32 != it2_crc32));
228   VIXL_CHECK(!(it3 != it3));
229   //   !(a == b)  <->  (a != b)
230   VIXL_CHECK(it0 != it1_neon);
231   VIXL_CHECK(it0 != it3);
232   VIXL_CHECK(it1_neon != it2_neon);
233   VIXL_CHECK(it1_neon != it2_crc32);
234   VIXL_CHECK(it1_neon != it3);
235   VIXL_CHECK(it2_neon != it2_crc32);
236   VIXL_CHECK(it3 != it0);
237   VIXL_CHECK(it3 != it1_neon);
238 
239   // Dereferenceable
240   VIXL_CHECK(*it0 == CPUFeatures::kNone);
241   VIXL_CHECK(*it1_neon == CPUFeatures::kNEON);
242   VIXL_CHECK(*it2_neon == CPUFeatures::kNEON);
243   VIXL_CHECK(*it2_crc32 == CPUFeatures::kCRC32);
244   VIXL_CHECK(*it3 == CPUFeatures::kNone);
245 
246 #if __cplusplus >= 201103L
247   VIXL_STATIC_ASSERT(std::is_copy_constructible<It>::value);
248   VIXL_STATIC_ASSERT(std::is_copy_assignable<It>::value);
249   VIXL_STATIC_ASSERT(std::is_destructible<It>::value);
250 #endif
251   // Copy constructable
252   It test0 = it0;
253   It test1 = it1_neon;
254   It test2(it2_neon);
255   VIXL_CHECK(test0 == It(NULL, CPUFeatures::kNone));
256   VIXL_CHECK(test1 == It(&f1, CPUFeatures::kNEON));
257   VIXL_CHECK(test2 == It(&f2, CPUFeatures::kNEON));
258 
259   // Copy assignable
260   test2 = it2_crc32;
261   VIXL_CHECK(test2 == It(&f2, CPUFeatures::kCRC32));
262 
263   // Incrementable
264   // - Incrementing has no effect on an empty CPUFeatures.
265   VIXL_CHECK(it3++ == CPUFeatures::kNone);
266   VIXL_CHECK(++it3 == CPUFeatures::kNone);
267   VIXL_CHECK(it3 == It(&f3, CPUFeatures::kNone));
268   // - Incrementing moves to the next feature, wrapping around (through kNone).
269   //   This test will need to be updated if the Feature enum is reordered.
270   VIXL_CHECK(it2_neon++ == CPUFeatures::kNEON);
271   VIXL_CHECK(it2_neon++ == CPUFeatures::kCRC32);
272   VIXL_CHECK(it2_neon++ == CPUFeatures::kNone);
273   VIXL_CHECK(it2_neon++ == CPUFeatures::kFP);
274   VIXL_CHECK(it2_neon == It(&f2, CPUFeatures::kNEON));
275   VIXL_CHECK(++it2_crc32 == CPUFeatures::kNone);
276   VIXL_CHECK(++it2_crc32 == CPUFeatures::kFP);
277   VIXL_CHECK(++it2_crc32 == CPUFeatures::kNEON);
278   VIXL_CHECK(++it2_crc32 == CPUFeatures::kCRC32);
279   VIXL_CHECK(it2_crc32 == It(&f2, CPUFeatures::kCRC32));
280 }
281 
282 
TEST(CPUFeatures_iterator_loops)283 TEST(CPUFeatures_iterator_loops) {
284   // Check that CPUFeaturesIterator can be used for some simple loops.
285 
286   // Arbitrary feature lists.
287   CPUFeatures f1(CPUFeatures::kFP, CPUFeatures::kNEON);
288   CPUFeatures f2(CPUFeatures::kFP, CPUFeatures::kNEON, CPUFeatures::kCRC32);
289   CPUFeatures f3;
290 
291   // This test will need to be updated if the Feature enum is reordered.
292 
293   std::vector<CPUFeatures::Feature> f1_list;
294   for (CPUFeatures::const_iterator it = f1.begin(); it != f1.end(); ++it) {
295     f1_list.push_back(*it);
296   }
297   VIXL_CHECK(f1_list.size() == 2);
298   VIXL_CHECK(f1_list[0] == CPUFeatures::kFP);
299   VIXL_CHECK(f1_list[1] == CPUFeatures::kNEON);
300 
301   std::vector<CPUFeatures::Feature> f2_list;
302   for (CPUFeatures::const_iterator it = f2.begin(); it != f2.end(); ++it) {
303     f2_list.push_back(*it);
304   }
305   VIXL_CHECK(f2_list.size() == 3);
306   VIXL_CHECK(f2_list[0] == CPUFeatures::kFP);
307   VIXL_CHECK(f2_list[1] == CPUFeatures::kNEON);
308   VIXL_CHECK(f2_list[2] == CPUFeatures::kCRC32);
309 
310   std::vector<CPUFeatures::Feature> f3_list;
311   for (CPUFeatures::const_iterator it = f3.begin(); it != f3.end(); ++it) {
312     f3_list.push_back(*it);
313   }
314   VIXL_CHECK(f3_list.size() == 0);
315 
316 #if __cplusplus >= 201103L
317   std::vector<CPUFeatures::Feature> f2_list_cxx11;
318   for (auto&& feature : f2) {
319     f2_list_cxx11.push_back(feature);
320   }
321   VIXL_CHECK(f2_list_cxx11.size() == 3);
322   VIXL_CHECK(f2_list_cxx11[0] == CPUFeatures::kFP);
323   VIXL_CHECK(f2_list_cxx11[1] == CPUFeatures::kNEON);
324   VIXL_CHECK(f2_list_cxx11[2] == CPUFeatures::kCRC32);
325 
326   std::vector<CPUFeatures::Feature> f3_list_cxx11;
327   for (auto&& feature : f3) {
328     f3_list_cxx11.push_back(feature);
329   }
330   VIXL_CHECK(f3_list_cxx11.size() == 0);
331 #endif
332 }
333 
334 
TEST(CPUFeatures_empty)335 TEST(CPUFeatures_empty) {
336   // A default-constructed CPUFeatures has no features enabled.
337   CPUFeatures f;
338   for (CPUFeatures::const_iterator it = f.begin(); it != f.end(); ++it) {
339     VIXL_ABORT();
340   }
341 }
342 
343 
CPUFeaturesFormatHelper(const char * expected,const CPUFeatures & features)344 static void CPUFeaturesFormatHelper(const char* expected,
345                                     const CPUFeatures& features) {
346   std::stringstream os;
347   os << features;
348   std::string os_str = os.str();
349   if (os_str != expected) {
350     std::cout << "Found: " << os_str << "\n";
351     std::cout << "Expected: " << expected << "\n";
352     VIXL_ABORT();
353   }
354 }
355 
356 
TEST(CPUFeatures_format)357 TEST(CPUFeatures_format) {
358   // Check that the debug output is complete and accurate.
359 
360   // Individual features.
361   CPUFeaturesFormatHelper("", CPUFeatures(CPUFeatures::kNone));
362   CPUFeaturesFormatHelper("FP", CPUFeatures(CPUFeatures::kFP));
363   CPUFeaturesFormatHelper("NEON", CPUFeatures(CPUFeatures::kNEON));
364   CPUFeaturesFormatHelper("AES", CPUFeatures(CPUFeatures::kAES));
365   CPUFeaturesFormatHelper("Pmull1Q", CPUFeatures(CPUFeatures::kPmull1Q));
366   CPUFeaturesFormatHelper("SHA1", CPUFeatures(CPUFeatures::kSHA1));
367   CPUFeaturesFormatHelper("SHA2", CPUFeatures(CPUFeatures::kSHA2));
368   CPUFeaturesFormatHelper("CRC32", CPUFeatures(CPUFeatures::kCRC32));
369 
370   // Combinations of (arbitrary) features.
371   // This test will need to be updated if the Feature enum is reordered.
372   CPUFeatures f(CPUFeatures::kFP, CPUFeatures::kNEON);
373   CPUFeaturesFormatHelper("FP, NEON", f);
374   f.Combine(CPUFeatures::kCRC32);
375   CPUFeaturesFormatHelper("FP, NEON, CRC32", f);
376   f.Combine(CPUFeatures::kFcma);
377   CPUFeaturesFormatHelper("FP, NEON, CRC32, Fcma", f);
378   f.Combine(CPUFeatures::kSHA1);
379   CPUFeaturesFormatHelper("FP, NEON, CRC32, SHA1, Fcma", f);
380 
381   CPUFeaturesFormatHelper(
382       "ID register emulation, "
383       // Armv8.0
384       "FP, NEON, CRC32, "
385       "AES, SHA1, SHA2, Pmull1Q, "
386       // Armv8.1
387       "Atomics, LORegions, RDM, "
388       // Armv8.2
389       "DotProduct, FPHalf, NEONHalf, RAS, DCPoP, SHA3, SHA512, SM3, SM4, "
390       // Armv8.3
391       "PAuth, PAuthQARMA, PAuthGeneric, PAuthGenericQARMA, JSCVT, RCpc, Fcma",
392       CPUFeatures::All());
393 }
394 
395 
CPUFeaturesPredefinedResultCheckHelper(const std::set<CPUFeatures::Feature> & unexpected,const std::set<CPUFeatures::Feature> & expected)396 static void CPUFeaturesPredefinedResultCheckHelper(
397     const std::set<CPUFeatures::Feature>& unexpected,
398     const std::set<CPUFeatures::Feature>& expected) {
399   // Print a helpful diagnostic before checking the result.
400   typedef std::set<CPUFeatures::Feature>::const_iterator It;
401   if (!unexpected.empty()) {
402     std::cout << "Unexpected features:\n";
403     for (It it = unexpected.begin(); it != unexpected.end(); ++it) {
404       std::cout << "  " << *it << "\n";
405     }
406   }
407   if (!expected.empty()) {
408     std::cout << "Missing features:\n";
409     for (It it = expected.begin(); it != expected.end(); ++it) {
410       std::cout << "  " << *it << "\n";
411     }
412   }
413   VIXL_CHECK(unexpected.empty() && expected.empty());
414 }
415 
416 
TEST(CPUFeatures_predefined_legacy)417 TEST(CPUFeatures_predefined_legacy) {
418   CPUFeatures f = CPUFeatures::AArch64LegacyBaseline();
419   std::set<CPUFeatures::Feature> unexpected;
420   std::set<CPUFeatures::Feature> expected;
421   expected.insert(CPUFeatures::kFP);
422   expected.insert(CPUFeatures::kNEON);
423   expected.insert(CPUFeatures::kCRC32);
424 
425   for (CPUFeatures::const_iterator it = f.begin(); it != f.end(); ++it) {
426     if (expected.erase(*it) == 0) unexpected.insert(*it);
427   }
428   CPUFeaturesPredefinedResultCheckHelper(unexpected, expected);
429 }
430 
431 
TEST(CPUFeatures_predefined_all)432 TEST(CPUFeatures_predefined_all) {
433   CPUFeatures f = CPUFeatures::All();
434   std::set<CPUFeatures::Feature> found;
435 
436   for (CPUFeatures::const_iterator it = f.begin(); it != f.end(); ++it) {
437     found.insert(*it);
438   }
439   VIXL_CHECK(found.size() == CPUFeatures::kNumberOfFeatures);
440 }
441 
442 // The CPUFeaturesScope constructor is templated, and needs an object which
443 // implements `CPUFeatures* GetCPUFeatures()`. This is normally something like
444 // the Assembler, but for the tests we use an architecture-independent wrapper.
445 class GetCPUFeaturesWrapper {
446  public:
GetCPUFeaturesWrapper(CPUFeatures * cpu_features)447   explicit GetCPUFeaturesWrapper(CPUFeatures* cpu_features)
448       : cpu_features_(cpu_features) {}
449 
GetCPUFeatures() const450   CPUFeatures* GetCPUFeatures() const { return cpu_features_; }
451 
452  private:
453   CPUFeatures* cpu_features_;
454 };
455 
TEST(CPUFeaturesScope)456 TEST(CPUFeaturesScope) {
457   // Test that CPUFeaturesScope properly preserves state.
458 
459   CPUFeatures cpu(CPUFeatures::kCRC32, CPUFeatures::kSHA1, CPUFeatures::kAES);
460   GetCPUFeaturesWrapper top_level(&cpu);
461 
462   const CPUFeatures original_outer = cpu;
463 
464   {  // Test setting both new and existing features.
465     CPUFeaturesScope outer(&top_level, CPUFeatures::kSHA2, CPUFeatures::kAES);
466     VIXL_CHECK(outer.GetCPUFeatures() == &cpu);
467     VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32,
468                        CPUFeatures::kSHA1,
469                        CPUFeatures::kSHA2,
470                        CPUFeatures::kAES));
471 
472     // Features can be added or removed directly, in the usual fashion.
473     // (The scope will restore their original status when it ends.)
474     cpu.Combine(CPUFeatures::kSHA1, CPUFeatures::kAtomics);
475     VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32,
476                        CPUFeatures::kSHA1,
477                        CPUFeatures::kSHA2,
478                        CPUFeatures::kAES));
479     VIXL_CHECK(cpu.Has(CPUFeatures::kAtomics));
480 
481     cpu.Remove(CPUFeatures::kSHA2, CPUFeatures::kAES);
482     VIXL_CHECK(!cpu.Has(CPUFeatures::kSHA2, CPUFeatures::kAES));
483     VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32,
484                        CPUFeatures::kSHA1,
485                        CPUFeatures::kAtomics));
486 
487     const CPUFeatures original_inner = cpu;
488 
489     // Scopes can be nested.
490     {
491       // A CPUFeaturesScope can be constructed from a CPUFeatures*, or any
492       // (non-local) object that implements `CPUFeatures* GetCPUFeatures()`.
493       // Typically, this would be an Assembler or MacroAssembler, but
494       // CPUFeatureScope itself provides this method, and allows the test to
495       // remain architecture-agnostic.
496 
497       CPUFeatures auth(CPUFeatures::kPAuth,
498                        CPUFeatures::kPAuthQARMA,
499                        CPUFeatures::kPAuthGeneric,
500                        CPUFeatures::kPAuthGenericQARMA);
501 
502       CPUFeaturesScope inner(&outer, auth);
503       VIXL_CHECK(inner.GetCPUFeatures() == &cpu);
504       VIXL_CHECK(cpu.Has(auth.With(CPUFeatures::kCRC32,
505                                    CPUFeatures::kSHA1,
506                                    CPUFeatures::kAtomics)));
507     }
508     // Check for equivalence.
509     VIXL_CHECK(cpu.Has(original_inner));
510     VIXL_CHECK(original_inner.Has(cpu));
511   }
512 
513   // Check for equivalence.
514   VIXL_CHECK(cpu.Has(original_outer));
515   VIXL_CHECK(original_outer.Has(cpu));
516 }
517 
518 }  // namespace vixl
519