blob: dfb61af9f646bd5e3c7adabe1d349c08c31baf00 [file] [log] [blame]
Pierre Langloisefe0c1f2016-11-24 11:54:47 +00001// 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
Jacob Bramley2af191d2018-05-16 10:22:44 +010027#include <iostream>
28#include <set>
29#include <sstream>
30#include <vector>
31
Jacob Bramley2af191d2018-05-16 10:22:44 +010032#include "cpu-features.h"
mmc28a1a2c1d32024-02-01 16:43:49 +000033#include "test-runner.h"
Pierre Langloisefe0c1f2016-11-24 11:54:47 +000034#include "utils-vixl.h"
35
Jacob Bramley2af191d2018-05-16 10:22:44 +010036#if __cplusplus >= 201103L
37#include <type_traits>
38#endif
39
Pierre Langloisefe0c1f2016-11-24 11:54:47 +000040#define TEST(name) TEST_(API_##name)
41
42#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
43
44namespace vixl {
45
46// Describe the result of a test. Should IsUintN() and IsIntN() return true or
47// false for N and X?
48template <typename T>
49struct 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.
57TEST(IsUint_IsInt) {
58 UintIntTest<uint32_t> test_little_values_unsigned[] = {
Pierre Langlois1bce0072017-06-06 17:58:58 +010059 {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)},
Pierre Langloisefe0c1f2016-11-24 11:54:47 +000067
Pierre Langlois1bce0072017-06-06 17:58:58 +010068 {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)},
Pierre Langloisefe0c1f2016-11-24 11:54:47 +000076 };
77
78 UintIntTest<int32_t> test_little_values_signed[] = {
Pierre Langlois1bce0072017-06-06 17:58:58 +010079 {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)},
Pierre Langloisefe0c1f2016-11-24 11:54:47 +000087
Pierre Langlois1bce0072017-06-06 17:58:58 +010088 {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)},
Pierre Langloisefe0c1f2016-11-24 11:54:47 +000096 };
97
98 UintIntTest<uint32_t> test_u16[] = {
Pierre Langloisbde2e4b2017-01-24 17:41:26 +000099 {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)},
Pierre Langloisefe0c1f2016-11-24 11:54:47 +0000107 };
108
109 UintIntTest<int32_t> test_i16[] = {
Pierre Langloisbde2e4b2017-01-24 17:41:26 +0000110 {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)},
Pierre Langloisefe0c1f2016-11-24 11:54:47 +0000118 };
119
120 UintIntTest<uint64_t> test_u32[] = {
Pierre Langloisbde2e4b2017-01-24 17:41:26 +0000121 {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)},
Pierre Langloisefe0c1f2016-11-24 11:54:47 +0000125 };
126
127 UintIntTest<int64_t> test_i32[] = {
Pierre Langloisbde2e4b2017-01-24 17:41:26 +0000128 {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))
Pierre Langloisefe0c1f2016-11-24 11:54:47 +0000134 };
135
136 UintIntTest<uint64_t> test_unsigned_higher_than_32[] = {
Pierre Langloisbde2e4b2017-01-24 17:41:26 +0000137 {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)},
Pierre Langloisefe0c1f2016-11-24 11:54:47 +0000141 };
142
143 UintIntTest<int64_t> test_signed_higher_than_32[] = {
Pierre Langloisbde2e4b2017-01-24 17:41:26 +0000144 {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))
Pierre Langloisefe0c1f2016-11-24 11:54:47 +0000148 };
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
Pierre Langloisbde2e4b2017-01-24 17:41:26 +0000161#define TEST_UINT(test_vector) \
Pierre Langloisefe0c1f2016-11-24 11:54:47 +0000162 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
Pierre Langloisbde2e4b2017-01-24 17:41:26 +0000170#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 } \
Pierre Langloisefe0c1f2016-11-24 11:54:47 +0000177 }
178
Pierre Langloisbde2e4b2017-01-24 17:41:26 +0000179 TEST_LIST(TEST_UINT)
180 TEST_LIST(TEST_INT)
Pierre Langloisefe0c1f2016-11-24 11:54:47 +0000181
182#undef TEST_UINT
183#undef TEST_INT
184
185#undef TEST_LIST
186}
187
Jacob Bramley2af191d2018-05-16 10:22:44 +0100188
189TEST(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.
Jacob Bramleycaa40ee2020-07-08 20:46:38 +0100264 VIXL_CHECK(*it3++ == CPUFeatures::kNone);
265 VIXL_CHECK(*(++it3) == CPUFeatures::kNone);
Jacob Bramley2af191d2018-05-16 10:22:44 +0100266 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.
Jacob Bramleycaa40ee2020-07-08 20:46:38 +0100269 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);
Jacob Bramley2af191d2018-05-16 10:22:44 +0100273 VIXL_CHECK(it2_neon == It(&f2, CPUFeatures::kNEON));
Jacob Bramleycaa40ee2020-07-08 20:46:38 +0100274 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);
Jacob Bramley2af191d2018-05-16 10:22:44 +0100278 VIXL_CHECK(it2_crc32 == It(&f2, CPUFeatures::kCRC32));
279}
280
281
282TEST(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
Jacob Bramley2af191d2018-05-16 10:22:44 +0100315 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);
Jacob Bramley2af191d2018-05-16 10:22:44 +0100329}
330
331
332TEST(CPUFeatures_empty) {
333 // A default-constructed CPUFeatures has no features enabled.
Jacob Bramleycaa40ee2020-07-08 20:46:38 +0100334 CPUFeatures features;
335 for (auto f : features) {
336 USE(f);
Jacob Bramley2af191d2018-05-16 10:22:44 +0100337 VIXL_ABORT();
338 }
Jacob Bramleycaa40ee2020-07-08 20:46:38 +0100339 VIXL_CHECK(features.HasNoFeatures());
340 VIXL_CHECK(features.Count() == 0);
Jacob Bramley2af191d2018-05-16 10:22:44 +0100341}
342
343
344static 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
357TEST(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);
Jacob Bramley2af191d2018-05-16 10:22:44 +0100380}
381
382
383static 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.
Jacob Bramley2af191d2018-05-16 10:22:44 +0100387 if (!unexpected.empty()) {
388 std::cout << "Unexpected features:\n";
Jacob Bramleycaa40ee2020-07-08 20:46:38 +0100389 for (auto f : unexpected) {
390 std::cout << " " << f << "\n";
Jacob Bramley2af191d2018-05-16 10:22:44 +0100391 }
392 }
393 if (!expected.empty()) {
394 std::cout << "Missing features:\n";
Jacob Bramleycaa40ee2020-07-08 20:46:38 +0100395 for (auto f : expected) {
396 std::cout << " " << f << "\n";
Jacob Bramley2af191d2018-05-16 10:22:44 +0100397 }
398 }
399 VIXL_CHECK(unexpected.empty() && expected.empty());
400}
401
402
403TEST(CPUFeatures_predefined_legacy) {
Jacob Bramleycaa40ee2020-07-08 20:46:38 +0100404 CPUFeatures features = CPUFeatures::AArch64LegacyBaseline();
Jacob Bramley2af191d2018-05-16 10:22:44 +0100405 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
Jacob Bramleycaa40ee2020-07-08 20:46:38 +0100411 for (auto f : features) {
412 if (expected.erase(f) == 0) unexpected.insert(f);
Jacob Bramley2af191d2018-05-16 10:22:44 +0100413 }
414 CPUFeaturesPredefinedResultCheckHelper(unexpected, expected);
415}
416
417
418TEST(CPUFeatures_predefined_all) {
Jacob Bramleycaa40ee2020-07-08 20:46:38 +0100419 CPUFeatures features = CPUFeatures::All();
Jacob Bramley2af191d2018-05-16 10:22:44 +0100420 std::set<CPUFeatures::Feature> found;
421
Jacob Bramleycaa40ee2020-07-08 20:46:38 +0100422 for (auto f : features) {
423 found.insert(f);
Jacob Bramley2af191d2018-05-16 10:22:44 +0100424 }
425 VIXL_CHECK(found.size() == CPUFeatures::kNumberOfFeatures);
Jacob Bramleycaa40ee2020-07-08 20:46:38 +0100426 VIXL_CHECK(found.size() == features.Count());
Jacob Bramley2af191d2018-05-16 10:22:44 +0100427}
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.
432class GetCPUFeaturesWrapper {
433 public:
434 explicit GetCPUFeaturesWrapper(CPUFeatures* cpu_features)
435 : cpu_features_(cpu_features) {}
436
437 CPUFeatures* GetCPUFeatures() const { return cpu_features_; }
438
439 private:
440 CPUFeatures* cpu_features_;
441};
442
443TEST(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,
Jacob Bramley8c4ceb62020-07-08 20:53:32 +0100487 CPUFeatures::kPAuthGenericQARMA,
488 CPUFeatures::kPAuthEnhancedPAC2,
489 CPUFeatures::kPAuthFPAC,
490 CPUFeatures::kPAuthFPACCombined);
Jacob Bramley2af191d2018-05-16 10:22:44 +0100491
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
Jacob Bramley8c4ceb62020-07-08 20:53:32 +0100503 {
504 // Scopes can be initialised with no features.
505 CPUFeaturesScope scope(&top_level);
506 }
507
Jacob Bramley2af191d2018-05-16 10:22:44 +0100508 // Check for equivalence.
509 VIXL_CHECK(cpu.Has(original_outer));
510 VIXL_CHECK(original_outer.Has(cpu));
511}
512
Jacob Bramley48934322019-02-04 18:27:53 +0000513TEST(CPUFeatures_infer_from_os) {
514 // Test that CPUFeatures::InferFromOS functions on supported platforms.
515 CPUFeatures os;
Jacob Bramley2fb52cb2019-04-16 15:22:31 +0100516 VIXL_ASSERT(os.HasNoFeatures());
Jacob Bramley48934322019-02-04 18:27:53 +0000517 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.
Jacob Bramley2fb52cb2019-04-16 15:22:31 +0100521 if (os.HasNoFeatures()) {
Jacob Bramley48934322019-02-04 18:27:53 +0000522 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
530TEST(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 {
Anthony Barbier88e1d032019-06-13 15:20:20 +0100552 // 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";
Jacob Bramley48934322019-02-04 18:27:53 +0000559 }
560}
561
Pierre Langloisefe0c1f2016-11-24 11:54:47 +0000562} // namespace vixl