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   std::vector<CPUFeatures::Feature> f2_list_cxx11;
317   for (auto&& feature : f2) {
318     f2_list_cxx11.push_back(feature);
319   }
320   VIXL_CHECK(f2_list_cxx11.size() == 3);
321   VIXL_CHECK(f2_list_cxx11[0] == CPUFeatures::kFP);
322   VIXL_CHECK(f2_list_cxx11[1] == CPUFeatures::kNEON);
323   VIXL_CHECK(f2_list_cxx11[2] == CPUFeatures::kCRC32);
324 
325   std::vector<CPUFeatures::Feature> f3_list_cxx11;
326   for (auto&& feature : f3) {
327     f3_list_cxx11.push_back(feature);
328   }
329   VIXL_CHECK(f3_list_cxx11.size() == 0);
330 }
331 
332 
TEST(CPUFeatures_empty)333 TEST(CPUFeatures_empty) {
334   // A default-constructed CPUFeatures has no features enabled.
335   CPUFeatures features;
336   for (auto f : features) {
337     USE(f);
338     VIXL_ABORT();
339   }
340   VIXL_CHECK(features.HasNoFeatures());
341   VIXL_CHECK(features.Count() == 0);
342 }
343 
344 
CPUFeaturesFormatHelper(const char * expected,const CPUFeatures & features)345 static void CPUFeaturesFormatHelper(const char* expected,
346                                     const CPUFeatures& features) {
347   std::stringstream os;
348   os << features;
349   std::string os_str = os.str();
350   if (os_str != expected) {
351     std::cout << "Found: " << os_str << "\n";
352     std::cout << "Expected: " << expected << "\n";
353     VIXL_ABORT();
354   }
355 }
356 
357 
TEST(CPUFeatures_format)358 TEST(CPUFeatures_format) {
359   // Check that the debug output is complete and accurate.
360 
361   // Individual features.
362   CPUFeaturesFormatHelper("", CPUFeatures(CPUFeatures::kNone));
363   CPUFeaturesFormatHelper("FP", CPUFeatures(CPUFeatures::kFP));
364   CPUFeaturesFormatHelper("NEON", CPUFeatures(CPUFeatures::kNEON));
365   CPUFeaturesFormatHelper("AES", CPUFeatures(CPUFeatures::kAES));
366   CPUFeaturesFormatHelper("Pmull1Q", CPUFeatures(CPUFeatures::kPmull1Q));
367   CPUFeaturesFormatHelper("SHA1", CPUFeatures(CPUFeatures::kSHA1));
368   CPUFeaturesFormatHelper("SHA2", CPUFeatures(CPUFeatures::kSHA2));
369   CPUFeaturesFormatHelper("CRC32", CPUFeatures(CPUFeatures::kCRC32));
370 
371   // Combinations of (arbitrary) features.
372   // This test will need to be updated if the Feature enum is reordered.
373   CPUFeatures f(CPUFeatures::kFP, CPUFeatures::kNEON);
374   CPUFeaturesFormatHelper("FP, NEON", f);
375   f.Combine(CPUFeatures::kCRC32);
376   CPUFeaturesFormatHelper("FP, NEON, CRC32", f);
377   f.Combine(CPUFeatures::kFcma);
378   CPUFeaturesFormatHelper("FP, NEON, CRC32, Fcma", f);
379   f.Combine(CPUFeatures::kSHA1);
380   CPUFeaturesFormatHelper("FP, NEON, CRC32, SHA1, Fcma", f);
381 }
382 
383 
CPUFeaturesPredefinedResultCheckHelper(const std::set<CPUFeatures::Feature> & unexpected,const std::set<CPUFeatures::Feature> & expected)384 static void CPUFeaturesPredefinedResultCheckHelper(
385     const std::set<CPUFeatures::Feature>& unexpected,
386     const std::set<CPUFeatures::Feature>& expected) {
387   // Print a helpful diagnostic before checking the result.
388   if (!unexpected.empty()) {
389     std::cout << "Unexpected features:\n";
390     for (auto f : unexpected) {
391       std::cout << "  " << f << "\n";
392     }
393   }
394   if (!expected.empty()) {
395     std::cout << "Missing features:\n";
396     for (auto f : expected) {
397       std::cout << "  " << f << "\n";
398     }
399   }
400   VIXL_CHECK(unexpected.empty() && expected.empty());
401 }
402 
403 
TEST(CPUFeatures_predefined_legacy)404 TEST(CPUFeatures_predefined_legacy) {
405   CPUFeatures features = CPUFeatures::AArch64LegacyBaseline();
406   std::set<CPUFeatures::Feature> unexpected;
407   std::set<CPUFeatures::Feature> expected;
408   expected.insert(CPUFeatures::kFP);
409   expected.insert(CPUFeatures::kNEON);
410   expected.insert(CPUFeatures::kCRC32);
411 
412   for (auto f : features) {
413     if (expected.erase(f) == 0) unexpected.insert(f);
414   }
415   CPUFeaturesPredefinedResultCheckHelper(unexpected, expected);
416 }
417 
418 
TEST(CPUFeatures_predefined_all)419 TEST(CPUFeatures_predefined_all) {
420   CPUFeatures features = CPUFeatures::All();
421   std::set<CPUFeatures::Feature> found;
422 
423   for (auto f : features) {
424     found.insert(f);
425   }
426   VIXL_CHECK(found.size() == CPUFeatures::kNumberOfFeatures);
427   VIXL_CHECK(found.size() == features.Count());
428 }
429 
430 // The CPUFeaturesScope constructor is templated, and needs an object which
431 // implements `CPUFeatures* GetCPUFeatures()`. This is normally something like
432 // the Assembler, but for the tests we use an architecture-independent wrapper.
433 class GetCPUFeaturesWrapper {
434  public:
GetCPUFeaturesWrapper(CPUFeatures * cpu_features)435   explicit GetCPUFeaturesWrapper(CPUFeatures* cpu_features)
436       : cpu_features_(cpu_features) {}
437 
GetCPUFeatures() const438   CPUFeatures* GetCPUFeatures() const { return cpu_features_; }
439 
440  private:
441   CPUFeatures* cpu_features_;
442 };
443 
TEST(CPUFeaturesScope)444 TEST(CPUFeaturesScope) {
445   // Test that CPUFeaturesScope properly preserves state.
446 
447   CPUFeatures cpu(CPUFeatures::kCRC32, CPUFeatures::kSHA1, CPUFeatures::kAES);
448   GetCPUFeaturesWrapper top_level(&cpu);
449 
450   const CPUFeatures original_outer = cpu;
451 
452   {  // Test setting both new and existing features.
453     CPUFeaturesScope outer(&top_level, CPUFeatures::kSHA2, CPUFeatures::kAES);
454     VIXL_CHECK(outer.GetCPUFeatures() == &cpu);
455     VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32,
456                        CPUFeatures::kSHA1,
457                        CPUFeatures::kSHA2,
458                        CPUFeatures::kAES));
459 
460     // Features can be added or removed directly, in the usual fashion.
461     // (The scope will restore their original status when it ends.)
462     cpu.Combine(CPUFeatures::kSHA1, CPUFeatures::kAtomics);
463     VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32,
464                        CPUFeatures::kSHA1,
465                        CPUFeatures::kSHA2,
466                        CPUFeatures::kAES));
467     VIXL_CHECK(cpu.Has(CPUFeatures::kAtomics));
468 
469     cpu.Remove(CPUFeatures::kSHA2, CPUFeatures::kAES);
470     VIXL_CHECK(!cpu.Has(CPUFeatures::kSHA2, CPUFeatures::kAES));
471     VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32,
472                        CPUFeatures::kSHA1,
473                        CPUFeatures::kAtomics));
474 
475     const CPUFeatures original_inner = cpu;
476 
477     // Scopes can be nested.
478     {
479       // A CPUFeaturesScope can be constructed from a CPUFeatures*, or any
480       // (non-local) object that implements `CPUFeatures* GetCPUFeatures()`.
481       // Typically, this would be an Assembler or MacroAssembler, but
482       // CPUFeatureScope itself provides this method, and allows the test to
483       // remain architecture-agnostic.
484 
485       CPUFeatures auth(CPUFeatures::kPAuth,
486                        CPUFeatures::kPAuthQARMA,
487                        CPUFeatures::kPAuthGeneric,
488                        CPUFeatures::kPAuthGenericQARMA,
489                        CPUFeatures::kPAuthEnhancedPAC2,
490                        CPUFeatures::kPAuthFPAC,
491                        CPUFeatures::kPAuthFPACCombined);
492 
493       CPUFeaturesScope inner(&outer, auth);
494       VIXL_CHECK(inner.GetCPUFeatures() == &cpu);
495       VIXL_CHECK(cpu.Has(auth.With(CPUFeatures::kCRC32,
496                                    CPUFeatures::kSHA1,
497                                    CPUFeatures::kAtomics)));
498     }
499     // Check for equivalence.
500     VIXL_CHECK(cpu.Has(original_inner));
501     VIXL_CHECK(original_inner.Has(cpu));
502   }
503 
504   {
505     // Scopes can be initialised with no features.
506     CPUFeaturesScope scope(&top_level);
507   }
508 
509   // Check for equivalence.
510   VIXL_CHECK(cpu.Has(original_outer));
511   VIXL_CHECK(original_outer.Has(cpu));
512 }
513 
TEST(CPUFeatures_infer_from_os)514 TEST(CPUFeatures_infer_from_os) {
515   // Test that CPUFeatures::InferFromOS functions on supported platforms.
516   CPUFeatures os;
517   VIXL_ASSERT(os.HasNoFeatures());
518   os = CPUFeatures::InferFromOS();
519 
520   // Every real platform has FP and NEON. However, InferFromOS does not support
521   // every platform, so we also have to tolerate empty results.
522   if (os.HasNoFeatures()) {
523     std::cout << "Warning: CPUFeatures::InferFromOS() returned no results.\n";
524   } else {
525     std::cout << "CPUFeatures::InferFromOS():\n  {" << os << "}\n";
526     VIXL_CHECK(os.Has(CPUFeatures::kFP));
527     VIXL_CHECK(os.Has(CPUFeatures::kNEON));
528   }
529 }
530 
TEST(CPUFeatures_infer_from_id_registers)531 TEST(CPUFeatures_infer_from_id_registers) {
532   CPUFeatures os_only =
533       CPUFeatures::InferFromOS(CPUFeatures::kDontQueryIDRegisters);
534   std::cout << "CPUFeatures::InferFromOS(kDontQueryIDRegisters):\n  {"
535             << os_only << "}\n";
536   if (os_only.Has(CPUFeatures::kIDRegisterEmulation)) {
537     CPUFeatures id_regs = CPUFeatures::InferFromIDRegisters();
538     std::cout << "CPUFeatures::InferFromIDRegisters():\n  {" << id_regs
539               << "}\n";
540     // The ID registers should return at least as many features as the OS
541     // information. This is intended to verify VIXL's InferFromIDRegisters
542     // logic, but it also relies on the OS presenting consistent information.
543     VIXL_CHECK(id_regs.Has(os_only));
544 
545     // The default InferFromOS should combine its results with
546     // InferFromIDRegisters.
547     CPUFeatures os_auto = CPUFeatures::InferFromOS();
548     CPUFeatures os_with_id_regs = os_only.With(id_regs);
549     // Check equivalence.
550     VIXL_CHECK(os_auto.Has(os_with_id_regs));
551     VIXL_CHECK(os_with_id_regs.Has(os_auto));
552   } else {
553     // Note: This message needs to match REGEXP_MISSING_FEATURES from
554     // tools/threaded_test.py.
555     std::cout << "SKIPPED: Missing features: { "
556               << CPUFeatures::kIDRegisterEmulation << " }\n";
557     std::cout << "This test requires the following features to run its "
558                  "generated code on this CPU: "
559               << CPUFeatures::kIDRegisterEmulation << "\n";
560   }
561 }
562 
563 }  // namespace vixl
564