Home | History | Annotate | Download | only in test
      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.
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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:
    447   explicit GetCPUFeaturesWrapper(CPUFeatures* cpu_features)
    448       : cpu_features_(cpu_features) {}
    449 
    450   CPUFeatures* GetCPUFeatures() const { return cpu_features_; }
    451 
    452  private:
    453   CPUFeatures* cpu_features_;
    454 };
    455 
    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