• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "cpu-features.h"
33 #include "test-runner.h"
34 #include "utils-vixl.h"
35 
36 #if __cplusplus >= 201103L
37 #include <type_traits>
38 #endif
39 
40 #define TEST(name) TEST_(API_##name)
41 
42 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
43 
44 namespace vixl {
45 
46 // Describe the result of a test. Should IsUintN() and IsIntN() return true or
47 // false for N and X?
48 template <typename T>
49 struct UintIntTest {
50   bool is_uintn;
51   bool is_intn;
52   unsigned n;
53   T x;
54 };
55 
56 // Test IsUintN() and IsIntN() against various values and integral types.
TEST(IsUint_IsInt)57 TEST(IsUint_IsInt) {
58   UintIntTest<uint32_t> test_little_values_unsigned[] = {
59       {true, true, 1, UINT32_C(0x0)},   {true, false, 1, UINT32_C(0x1)},
60       {false, false, 1, UINT32_C(0x2)}, {false, false, 1, UINT32_C(0x3)},
61       {false, false, 1, UINT32_C(0x4)}, {false, false, 1, UINT32_C(0x5)},
62       {false, false, 1, UINT32_C(0x6)}, {false, false, 1, UINT32_C(0x7)},
63       {false, false, 1, UINT32_C(0x8)}, {false, false, 1, UINT32_C(0x9)},
64       {false, false, 1, UINT32_C(0xa)}, {false, false, 1, UINT32_C(0xb)},
65       {false, false, 1, UINT32_C(0xc)}, {false, false, 1, UINT32_C(0xd)},
66       {false, false, 1, UINT32_C(0xe)}, {false, false, 1, UINT32_C(0xf)},
67 
68       {true, true, 2, UINT32_C(0x0)},   {true, true, 2, UINT32_C(0x1)},
69       {true, false, 2, UINT32_C(0x2)},  {true, false, 2, UINT32_C(0x3)},
70       {false, false, 2, UINT32_C(0x4)}, {false, false, 2, UINT32_C(0x5)},
71       {false, false, 2, UINT32_C(0x6)}, {false, false, 2, UINT32_C(0x7)},
72       {false, false, 2, UINT32_C(0x8)}, {false, false, 2, UINT32_C(0x9)},
73       {false, false, 2, UINT32_C(0xa)}, {false, false, 2, UINT32_C(0xb)},
74       {false, false, 2, UINT32_C(0xc)}, {false, false, 2, UINT32_C(0xd)},
75       {false, false, 2, UINT32_C(0xe)}, {false, false, 2, UINT32_C(0xf)},
76   };
77 
78   UintIntTest<int32_t> test_little_values_signed[] = {
79       {true, true, 1, INT32_C(0)},    {true, false, 1, INT32_C(1)},
80       {false, false, 1, INT32_C(2)},  {false, false, 1, INT32_C(3)},
81       {false, false, 1, INT32_C(4)},  {false, false, 1, INT32_C(5)},
82       {false, false, 1, INT32_C(6)},  {false, false, 1, INT32_C(7)},
83       {false, true, 1, INT32_C(-1)},  {false, false, 1, INT32_C(-2)},
84       {false, false, 1, INT32_C(-3)}, {false, false, 1, INT32_C(-4)},
85       {false, false, 1, INT32_C(-5)}, {false, false, 1, INT32_C(-6)},
86       {false, false, 1, INT32_C(-7)}, {false, false, 1, INT32_C(-8)},
87 
88       {true, true, 2, INT32_C(0)},    {true, true, 2, INT32_C(1)},
89       {true, false, 2, INT32_C(2)},   {true, false, 2, INT32_C(3)},
90       {false, false, 2, INT32_C(4)},  {false, false, 2, INT32_C(5)},
91       {false, false, 2, INT32_C(6)},  {false, false, 2, INT32_C(7)},
92       {false, true, 2, INT32_C(-1)},  {false, true, 2, INT32_C(-2)},
93       {false, false, 2, INT32_C(-3)}, {false, false, 2, INT32_C(-4)},
94       {false, false, 2, INT32_C(-5)}, {false, false, 2, INT32_C(-6)},
95       {false, false, 2, INT32_C(-7)}, {false, false, 2, INT32_C(-8)},
96   };
97 
98   UintIntTest<uint32_t> test_u16[] = {
99       {true, true, 16, UINT32_C(0x0)},
100       {true, false, 16, UINT32_C(0xabcd)},
101       {true, false, 16, UINT32_C(0x8000)},
102       {true, false, 16, UINT32_C(0xffff)},
103       {false, false, 16, UINT32_C(0x10000)},
104       {false, false, 16, UINT32_C(0xffff0000)},
105       {false, false, 16, UINT32_C(0xffff8000)},
106       {false, false, 16, UINT32_C(0xffffffff)},
107   };
108 
109   UintIntTest<int32_t> test_i16[] = {
110       {true, true, 16, INT32_C(0x0)},
111       {true, false, 16, INT32_C(0xabcd)},
112       {true, false, 16, INT32_C(0x8000)},
113       {true, false, 16, INT32_C(0xffff)},
114       {false, false, 16, INT32_C(0x10000)},
115       {true, true, 16, INT32_C(42)},
116       {false, true, 16, INT32_C(-42)},
117       {false, true, 16, INT32_C(-1)},
118   };
119 
120   UintIntTest<uint64_t> test_u32[] = {
121       {true, true, 32, UINT64_C(0x0)},
122       {true, false, 32, UINT64_C(0xabcdabcd)},
123       {true, false, 32, UINT64_C(0x80000000)},
124       {true, false, 32, UINT64_C(0xffffffff)},
125   };
126 
127   UintIntTest<int64_t> test_i32[] = {
128       {true, true, 32, INT64_C(0)},
129       {true, true, 32, INT64_C(42)},
130       {false, true, 32, INT64_C(-42)},
131       {false, true, 32, INT64_C(-1)},
132       {true, true, 32, INT64_C(2147483647)},    // (1 << (32 - 1)) - 1
133       {false, true, 32, INT64_C(-2147483648)},  // -(1 << (32 - 1))
134   };
135 
136   UintIntTest<uint64_t> test_unsigned_higher_than_32[] = {
137       {false, false, 54, UINT64_C(0xabcdef9012345678)},
138       {true, false, 33, UINT64_C(0x100000000)},
139       {true, false, 62, UINT64_C(0x3fffffffffffffff)},
140       {true, false, 63, UINT64_C(0x7fffffffffffffff)},
141   };
142 
143   UintIntTest<int64_t> test_signed_higher_than_32[] = {
144       {true, true, 54, INT64_C(9007199254740991)},   // (1 << (54 - 1)) - 1
145       {true, false, 54, INT64_C(9007199254740992)},  // 1 << (54 - 1)
146       {true, true, 33, INT64_C(4294967295)},         // (1 << (33 - 1) - 1)
147       {false, true, 33, INT64_C(-4294967296)},       // -(1 << (33 - 1))
148   };
149 
150 #define TEST_LIST(M)              \
151   M(test_little_values_unsigned)  \
152   M(test_little_values_signed)    \
153   M(test_u16)                     \
154   M(test_i16)                     \
155   M(test_u32)                     \
156   M(test_i32)                     \
157   M(test_unsigned_higher_than_32) \
158   M(test_signed_higher_than_32)
159 
160 
161 #define TEST_UINT(test_vector)                                  \
162   for (unsigned i = 0; i < ARRAY_SIZE(test_vector); i++) {      \
163     if (test_vector[i].is_uintn) {                              \
164       VIXL_CHECK(IsUintN(test_vector[i].n, test_vector[i].x));  \
165     } else {                                                    \
166       VIXL_CHECK(!IsUintN(test_vector[i].n, test_vector[i].x)); \
167     }                                                           \
168   }
169 
170 #define TEST_INT(test_vector)                                  \
171   for (unsigned i = 0; i < ARRAY_SIZE(test_vector); i++) {     \
172     if (test_vector[i].is_intn) {                              \
173       VIXL_CHECK(IsIntN(test_vector[i].n, test_vector[i].x));  \
174     } else {                                                   \
175       VIXL_CHECK(!IsIntN(test_vector[i].n, test_vector[i].x)); \
176     }                                                          \
177   }
178 
179   TEST_LIST(TEST_UINT)
180   TEST_LIST(TEST_INT)
181 
182 #undef TEST_UINT
183 #undef TEST_INT
184 
185 #undef TEST_LIST
186 }
187 
188 
TEST(CPUFeatures_iterator_api)189 TEST(CPUFeatures_iterator_api) {
190   // CPUFeaturesIterator does not fully satisfy the requirements of C++'s
191   // iterator concepts, but it should implement enough for some basic usage.
192 
193   // Arbitrary feature lists.
194   CPUFeatures f1(CPUFeatures::kFP, CPUFeatures::kNEON);
195   CPUFeatures f2(CPUFeatures::kFP, CPUFeatures::kNEON, CPUFeatures::kCRC32);
196   CPUFeatures f3;
197 
198   typedef CPUFeatures::const_iterator It;
199 
200   It it0;
201   It it1_neon(&f1, CPUFeatures::kNEON);
202   It it2_neon(&f2, CPUFeatures::kNEON);
203   It it2_crc32(&f2, CPUFeatures::kCRC32);
204   It it3(&f3);
205 
206   // Equality
207   VIXL_CHECK(it0 == it0);
208   VIXL_CHECK(it1_neon == it1_neon);
209   VIXL_CHECK(it2_neon == it2_neon);
210   VIXL_CHECK(it2_crc32 == it2_crc32);
211   VIXL_CHECK(it3 == it3);
212   VIXL_CHECK(!(it0 == it1_neon));
213   VIXL_CHECK(!(it0 == it3));
214   VIXL_CHECK(!(it1_neon == it2_neon));
215   VIXL_CHECK(!(it1_neon == it2_crc32));
216   VIXL_CHECK(!(it1_neon == it3));
217   VIXL_CHECK(!(it2_neon == it2_crc32));
218   VIXL_CHECK(!(it3 == it0));
219   VIXL_CHECK(!(it3 == it1_neon));
220 
221   // Inequality
222   //   (a == b)  <->  !(a != b)
223   VIXL_CHECK(!(it0 != it0));
224   VIXL_CHECK(!(it1_neon != it1_neon));
225   VIXL_CHECK(!(it2_neon != it2_neon));
226   VIXL_CHECK(!(it2_crc32 != it2_crc32));
227   VIXL_CHECK(!(it3 != it3));
228   //   !(a == b)  <->  (a != b)
229   VIXL_CHECK(it0 != it1_neon);
230   VIXL_CHECK(it0 != it3);
231   VIXL_CHECK(it1_neon != it2_neon);
232   VIXL_CHECK(it1_neon != it2_crc32);
233   VIXL_CHECK(it1_neon != it3);
234   VIXL_CHECK(it2_neon != it2_crc32);
235   VIXL_CHECK(it3 != it0);
236   VIXL_CHECK(it3 != it1_neon);
237 
238   // Dereferenceable
239   VIXL_CHECK(*it0 == CPUFeatures::kNone);
240   VIXL_CHECK(*it1_neon == CPUFeatures::kNEON);
241   VIXL_CHECK(*it2_neon == CPUFeatures::kNEON);
242   VIXL_CHECK(*it2_crc32 == CPUFeatures::kCRC32);
243   VIXL_CHECK(*it3 == CPUFeatures::kNone);
244 
245 #if __cplusplus >= 201103L
246   VIXL_STATIC_ASSERT(std::is_copy_constructible<It>::value);
247   VIXL_STATIC_ASSERT(std::is_copy_assignable<It>::value);
248   VIXL_STATIC_ASSERT(std::is_destructible<It>::value);
249 #endif
250   // Copy constructable
251   It test0 = it0;
252   It test1 = it1_neon;
253   It test2(it2_neon);
254   VIXL_CHECK(test0 == It(NULL, CPUFeatures::kNone));
255   VIXL_CHECK(test1 == It(&f1, CPUFeatures::kNEON));
256   VIXL_CHECK(test2 == It(&f2, CPUFeatures::kNEON));
257 
258   // Copy assignable
259   test2 = it2_crc32;
260   VIXL_CHECK(test2 == It(&f2, CPUFeatures::kCRC32));
261 
262   // Incrementable
263   // - Incrementing has no effect on an empty CPUFeatures.
264   VIXL_CHECK(*it3++ == CPUFeatures::kNone);
265   VIXL_CHECK(*(++it3) == CPUFeatures::kNone);
266   VIXL_CHECK(it3 == It(&f3, CPUFeatures::kNone));
267   // - Incrementing moves to the next feature, wrapping around (through kNone).
268   //   This test will need to be updated if the Feature enum is reordered.
269   VIXL_CHECK(*it2_neon++ == CPUFeatures::kNEON);
270   VIXL_CHECK(*it2_neon++ == CPUFeatures::kCRC32);
271   VIXL_CHECK(*it2_neon++ == CPUFeatures::kNone);
272   VIXL_CHECK(*it2_neon++ == CPUFeatures::kFP);
273   VIXL_CHECK(it2_neon == It(&f2, CPUFeatures::kNEON));
274   VIXL_CHECK(*(++it2_crc32) == CPUFeatures::kNone);
275   VIXL_CHECK(*(++it2_crc32) == CPUFeatures::kFP);
276   VIXL_CHECK(*(++it2_crc32) == CPUFeatures::kNEON);
277   VIXL_CHECK(*(++it2_crc32) == CPUFeatures::kCRC32);
278   VIXL_CHECK(it2_crc32 == It(&f2, CPUFeatures::kCRC32));
279 }
280 
281 
TEST(CPUFeatures_iterator_loops)282 TEST(CPUFeatures_iterator_loops) {
283   // Check that CPUFeaturesIterator can be used for some simple loops.
284 
285   // Arbitrary feature lists.
286   CPUFeatures f1(CPUFeatures::kFP, CPUFeatures::kNEON);
287   CPUFeatures f2(CPUFeatures::kFP, CPUFeatures::kNEON, CPUFeatures::kCRC32);
288   CPUFeatures f3;
289 
290   // This test will need to be updated if the Feature enum is reordered.
291 
292   std::vector<CPUFeatures::Feature> f1_list;
293   for (CPUFeatures::const_iterator it = f1.begin(); it != f1.end(); ++it) {
294     f1_list.push_back(*it);
295   }
296   VIXL_CHECK(f1_list.size() == 2);
297   VIXL_CHECK(f1_list[0] == CPUFeatures::kFP);
298   VIXL_CHECK(f1_list[1] == CPUFeatures::kNEON);
299 
300   std::vector<CPUFeatures::Feature> f2_list;
301   for (CPUFeatures::const_iterator it = f2.begin(); it != f2.end(); ++it) {
302     f2_list.push_back(*it);
303   }
304   VIXL_CHECK(f2_list.size() == 3);
305   VIXL_CHECK(f2_list[0] == CPUFeatures::kFP);
306   VIXL_CHECK(f2_list[1] == CPUFeatures::kNEON);
307   VIXL_CHECK(f2_list[2] == CPUFeatures::kCRC32);
308 
309   std::vector<CPUFeatures::Feature> f3_list;
310   for (CPUFeatures::const_iterator it = f3.begin(); it != f3.end(); ++it) {
311     f3_list.push_back(*it);
312   }
313   VIXL_CHECK(f3_list.size() == 0);
314 
315   std::vector<CPUFeatures::Feature> f2_list_cxx11;
316   for (auto&& feature : f2) {
317     f2_list_cxx11.push_back(feature);
318   }
319   VIXL_CHECK(f2_list_cxx11.size() == 3);
320   VIXL_CHECK(f2_list_cxx11[0] == CPUFeatures::kFP);
321   VIXL_CHECK(f2_list_cxx11[1] == CPUFeatures::kNEON);
322   VIXL_CHECK(f2_list_cxx11[2] == CPUFeatures::kCRC32);
323 
324   std::vector<CPUFeatures::Feature> f3_list_cxx11;
325   for (auto&& feature : f3) {
326     f3_list_cxx11.push_back(feature);
327   }
328   VIXL_CHECK(f3_list_cxx11.size() == 0);
329 }
330 
331 
TEST(CPUFeatures_empty)332 TEST(CPUFeatures_empty) {
333   // A default-constructed CPUFeatures has no features enabled.
334   CPUFeatures features;
335   for (auto f : features) {
336     USE(f);
337     VIXL_ABORT();
338   }
339   VIXL_CHECK(features.HasNoFeatures());
340   VIXL_CHECK(features.Count() == 0);
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 
382 
CPUFeaturesPredefinedResultCheckHelper(const std::set<CPUFeatures::Feature> & unexpected,const std::set<CPUFeatures::Feature> & expected)383 static void CPUFeaturesPredefinedResultCheckHelper(
384     const std::set<CPUFeatures::Feature>& unexpected,
385     const std::set<CPUFeatures::Feature>& expected) {
386   // Print a helpful diagnostic before checking the result.
387   if (!unexpected.empty()) {
388     std::cout << "Unexpected features:\n";
389     for (auto f : unexpected) {
390       std::cout << "  " << f << "\n";
391     }
392   }
393   if (!expected.empty()) {
394     std::cout << "Missing features:\n";
395     for (auto f : expected) {
396       std::cout << "  " << f << "\n";
397     }
398   }
399   VIXL_CHECK(unexpected.empty() && expected.empty());
400 }
401 
402 
TEST(CPUFeatures_predefined_legacy)403 TEST(CPUFeatures_predefined_legacy) {
404   CPUFeatures features = CPUFeatures::AArch64LegacyBaseline();
405   std::set<CPUFeatures::Feature> unexpected;
406   std::set<CPUFeatures::Feature> expected;
407   expected.insert(CPUFeatures::kFP);
408   expected.insert(CPUFeatures::kNEON);
409   expected.insert(CPUFeatures::kCRC32);
410 
411   for (auto f : features) {
412     if (expected.erase(f) == 0) unexpected.insert(f);
413   }
414   CPUFeaturesPredefinedResultCheckHelper(unexpected, expected);
415 }
416 
417 
TEST(CPUFeatures_predefined_all)418 TEST(CPUFeatures_predefined_all) {
419   CPUFeatures features = CPUFeatures::All();
420   std::set<CPUFeatures::Feature> found;
421 
422   for (auto f : features) {
423     found.insert(f);
424   }
425   VIXL_CHECK(found.size() == CPUFeatures::kNumberOfFeatures);
426   VIXL_CHECK(found.size() == features.Count());
427 }
428 
429 // The CPUFeaturesScope constructor is templated, and needs an object which
430 // implements `CPUFeatures* GetCPUFeatures()`. This is normally something like
431 // the Assembler, but for the tests we use an architecture-independent wrapper.
432 class GetCPUFeaturesWrapper {
433  public:
GetCPUFeaturesWrapper(CPUFeatures * cpu_features)434   explicit GetCPUFeaturesWrapper(CPUFeatures* cpu_features)
435       : cpu_features_(cpu_features) {}
436 
GetCPUFeatures() const437   CPUFeatures* GetCPUFeatures() const { return cpu_features_; }
438 
439  private:
440   CPUFeatures* cpu_features_;
441 };
442 
TEST(CPUFeaturesScope)443 TEST(CPUFeaturesScope) {
444   // Test that CPUFeaturesScope properly preserves state.
445 
446   CPUFeatures cpu(CPUFeatures::kCRC32, CPUFeatures::kSHA1, CPUFeatures::kAES);
447   GetCPUFeaturesWrapper top_level(&cpu);
448 
449   const CPUFeatures original_outer = cpu;
450 
451   {  // Test setting both new and existing features.
452     CPUFeaturesScope outer(&top_level, CPUFeatures::kSHA2, CPUFeatures::kAES);
453     VIXL_CHECK(outer.GetCPUFeatures() == &cpu);
454     VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32,
455                        CPUFeatures::kSHA1,
456                        CPUFeatures::kSHA2,
457                        CPUFeatures::kAES));
458 
459     // Features can be added or removed directly, in the usual fashion.
460     // (The scope will restore their original status when it ends.)
461     cpu.Combine(CPUFeatures::kSHA1, CPUFeatures::kAtomics);
462     VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32,
463                        CPUFeatures::kSHA1,
464                        CPUFeatures::kSHA2,
465                        CPUFeatures::kAES));
466     VIXL_CHECK(cpu.Has(CPUFeatures::kAtomics));
467 
468     cpu.Remove(CPUFeatures::kSHA2, CPUFeatures::kAES);
469     VIXL_CHECK(!cpu.Has(CPUFeatures::kSHA2, CPUFeatures::kAES));
470     VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32,
471                        CPUFeatures::kSHA1,
472                        CPUFeatures::kAtomics));
473 
474     const CPUFeatures original_inner = cpu;
475 
476     // Scopes can be nested.
477     {
478       // A CPUFeaturesScope can be constructed from a CPUFeatures*, or any
479       // (non-local) object that implements `CPUFeatures* GetCPUFeatures()`.
480       // Typically, this would be an Assembler or MacroAssembler, but
481       // CPUFeatureScope itself provides this method, and allows the test to
482       // remain architecture-agnostic.
483 
484       CPUFeatures auth(CPUFeatures::kPAuth,
485                        CPUFeatures::kPAuthQARMA,
486                        CPUFeatures::kPAuthGeneric,
487                        CPUFeatures::kPAuthGenericQARMA,
488                        CPUFeatures::kPAuthEnhancedPAC2,
489                        CPUFeatures::kPAuthFPAC,
490                        CPUFeatures::kPAuthFPACCombined);
491 
492       CPUFeaturesScope inner(&outer, auth);
493       VIXL_CHECK(inner.GetCPUFeatures() == &cpu);
494       VIXL_CHECK(cpu.Has(auth.With(CPUFeatures::kCRC32,
495                                    CPUFeatures::kSHA1,
496                                    CPUFeatures::kAtomics)));
497     }
498     // Check for equivalence.
499     VIXL_CHECK(cpu.Has(original_inner));
500     VIXL_CHECK(original_inner.Has(cpu));
501   }
502 
503   {
504     // Scopes can be initialised with no features.
505     CPUFeaturesScope scope(&top_level);
506   }
507 
508   // Check for equivalence.
509   VIXL_CHECK(cpu.Has(original_outer));
510   VIXL_CHECK(original_outer.Has(cpu));
511 }
512 
TEST(CPUFeatures_infer_from_os)513 TEST(CPUFeatures_infer_from_os) {
514   // Test that CPUFeatures::InferFromOS functions on supported platforms.
515   CPUFeatures os;
516   VIXL_ASSERT(os.HasNoFeatures());
517   os = CPUFeatures::InferFromOS();
518 
519   // Every real platform has FP and NEON. However, InferFromOS does not support
520   // every platform, so we also have to tolerate empty results.
521   if (os.HasNoFeatures()) {
522     std::cout << "Warning: CPUFeatures::InferFromOS() returned no results.\n";
523   } else {
524     std::cout << "CPUFeatures::InferFromOS():\n  {" << os << "}\n";
525     VIXL_CHECK(os.Has(CPUFeatures::kFP));
526     VIXL_CHECK(os.Has(CPUFeatures::kNEON));
527   }
528 }
529 
TEST(CPUFeatures_infer_from_id_registers)530 TEST(CPUFeatures_infer_from_id_registers) {
531   CPUFeatures os_only =
532       CPUFeatures::InferFromOS(CPUFeatures::kDontQueryIDRegisters);
533   std::cout << "CPUFeatures::InferFromOS(kDontQueryIDRegisters):\n  {"
534             << os_only << "}\n";
535   if (os_only.Has(CPUFeatures::kIDRegisterEmulation)) {
536     CPUFeatures id_regs = CPUFeatures::InferFromIDRegisters();
537     std::cout << "CPUFeatures::InferFromIDRegisters():\n  {" << id_regs
538               << "}\n";
539     // The ID registers should return at least as many features as the OS
540     // information. This is intended to verify VIXL's InferFromIDRegisters
541     // logic, but it also relies on the OS presenting consistent information.
542     VIXL_CHECK(id_regs.Has(os_only));
543 
544     // The default InferFromOS should combine its results with
545     // InferFromIDRegisters.
546     CPUFeatures os_auto = CPUFeatures::InferFromOS();
547     CPUFeatures os_with_id_regs = os_only.With(id_regs);
548     // Check equivalence.
549     VIXL_CHECK(os_auto.Has(os_with_id_regs));
550     VIXL_CHECK(os_with_id_regs.Has(os_auto));
551   } else {
552     // Note: This message needs to match REGEXP_MISSING_FEATURES from
553     // tools/threaded_test.py.
554     std::cout << "SKIPPED: Missing features: { "
555               << CPUFeatures::kIDRegisterEmulation << " }\n";
556     std::cout << "This test requires the following features to run its "
557                  "generated code on this CPU: "
558               << CPUFeatures::kIDRegisterEmulation << "\n";
559   }
560 }
561 
562 }  // namespace vixl
563