Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 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 | |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 27 | #include <iostream> |
| 28 | #include <set> |
| 29 | #include <sstream> |
| 30 | #include <vector> |
| 31 | |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 32 | #include "cpu-features.h" |
mmc28a | 1a2c1d3 | 2024-02-01 16:43:49 +0000 | [diff] [blame] | 33 | #include "test-runner.h" |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 34 | #include "utils-vixl.h" |
| 35 | |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 36 | #if __cplusplus >= 201103L |
| 37 | #include <type_traits> |
| 38 | #endif |
| 39 | |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 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. |
| 57 | TEST(IsUint_IsInt) { |
| 58 | UintIntTest<uint32_t> test_little_values_unsigned[] = { |
Pierre Langlois | 1bce007 | 2017-06-06 17:58:58 +0100 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 67 | |
Pierre Langlois | 1bce007 | 2017-06-06 17:58:58 +0100 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 76 | }; |
| 77 | |
| 78 | UintIntTest<int32_t> test_little_values_signed[] = { |
Pierre Langlois | 1bce007 | 2017-06-06 17:58:58 +0100 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 87 | |
Pierre Langlois | 1bce007 | 2017-06-06 17:58:58 +0100 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 96 | }; |
| 97 | |
| 98 | UintIntTest<uint32_t> test_u16[] = { |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 107 | }; |
| 108 | |
| 109 | UintIntTest<int32_t> test_i16[] = { |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 118 | }; |
| 119 | |
| 120 | UintIntTest<uint64_t> test_u32[] = { |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 125 | }; |
| 126 | |
| 127 | UintIntTest<int64_t> test_i32[] = { |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 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)) |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 134 | }; |
| 135 | |
| 136 | UintIntTest<uint64_t> test_unsigned_higher_than_32[] = { |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 141 | }; |
| 142 | |
| 143 | UintIntTest<int64_t> test_signed_higher_than_32[] = { |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 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)) |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 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 | |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 161 | #define TEST_UINT(test_vector) \ |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 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 | |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 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 | } \ |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 177 | } |
| 178 | |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 179 | TEST_LIST(TEST_UINT) |
| 180 | TEST_LIST(TEST_INT) |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 181 | |
| 182 | #undef TEST_UINT |
| 183 | #undef TEST_INT |
| 184 | |
| 185 | #undef TEST_LIST |
| 186 | } |
| 187 | |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 188 | |
| 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. |
Jacob Bramley | caa40ee | 2020-07-08 20:46:38 +0100 | [diff] [blame] | 264 | VIXL_CHECK(*it3++ == CPUFeatures::kNone); |
| 265 | VIXL_CHECK(*(++it3) == CPUFeatures::kNone); |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 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. |
Jacob Bramley | caa40ee | 2020-07-08 20:46:38 +0100 | [diff] [blame] | 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); |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 273 | VIXL_CHECK(it2_neon == It(&f2, CPUFeatures::kNEON)); |
Jacob Bramley | caa40ee | 2020-07-08 20:46:38 +0100 | [diff] [blame] | 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); |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 278 | VIXL_CHECK(it2_crc32 == It(&f2, CPUFeatures::kCRC32)); |
| 279 | } |
| 280 | |
| 281 | |
| 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 | |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 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); |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 329 | } |
| 330 | |
| 331 | |
| 332 | TEST(CPUFeatures_empty) { |
| 333 | // A default-constructed CPUFeatures has no features enabled. |
Jacob Bramley | caa40ee | 2020-07-08 20:46:38 +0100 | [diff] [blame] | 334 | CPUFeatures features; |
| 335 | for (auto f : features) { |
| 336 | USE(f); |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 337 | VIXL_ABORT(); |
| 338 | } |
Jacob Bramley | caa40ee | 2020-07-08 20:46:38 +0100 | [diff] [blame] | 339 | VIXL_CHECK(features.HasNoFeatures()); |
| 340 | VIXL_CHECK(features.Count() == 0); |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 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); |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 380 | } |
| 381 | |
| 382 | |
| 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. |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 387 | if (!unexpected.empty()) { |
| 388 | std::cout << "Unexpected features:\n"; |
Jacob Bramley | caa40ee | 2020-07-08 20:46:38 +0100 | [diff] [blame] | 389 | for (auto f : unexpected) { |
| 390 | std::cout << " " << f << "\n"; |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 391 | } |
| 392 | } |
| 393 | if (!expected.empty()) { |
| 394 | std::cout << "Missing features:\n"; |
Jacob Bramley | caa40ee | 2020-07-08 20:46:38 +0100 | [diff] [blame] | 395 | for (auto f : expected) { |
| 396 | std::cout << " " << f << "\n"; |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 397 | } |
| 398 | } |
| 399 | VIXL_CHECK(unexpected.empty() && expected.empty()); |
| 400 | } |
| 401 | |
| 402 | |
| 403 | TEST(CPUFeatures_predefined_legacy) { |
Jacob Bramley | caa40ee | 2020-07-08 20:46:38 +0100 | [diff] [blame] | 404 | CPUFeatures features = CPUFeatures::AArch64LegacyBaseline(); |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 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 | |
Jacob Bramley | caa40ee | 2020-07-08 20:46:38 +0100 | [diff] [blame] | 411 | for (auto f : features) { |
| 412 | if (expected.erase(f) == 0) unexpected.insert(f); |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 413 | } |
| 414 | CPUFeaturesPredefinedResultCheckHelper(unexpected, expected); |
| 415 | } |
| 416 | |
| 417 | |
| 418 | TEST(CPUFeatures_predefined_all) { |
Jacob Bramley | caa40ee | 2020-07-08 20:46:38 +0100 | [diff] [blame] | 419 | CPUFeatures features = CPUFeatures::All(); |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 420 | std::set<CPUFeatures::Feature> found; |
| 421 | |
Jacob Bramley | caa40ee | 2020-07-08 20:46:38 +0100 | [diff] [blame] | 422 | for (auto f : features) { |
| 423 | found.insert(f); |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 424 | } |
| 425 | VIXL_CHECK(found.size() == CPUFeatures::kNumberOfFeatures); |
Jacob Bramley | caa40ee | 2020-07-08 20:46:38 +0100 | [diff] [blame] | 426 | VIXL_CHECK(found.size() == features.Count()); |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 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: |
| 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 | |
| 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, |
Jacob Bramley | 8c4ceb6 | 2020-07-08 20:53:32 +0100 | [diff] [blame] | 487 | CPUFeatures::kPAuthGenericQARMA, |
| 488 | CPUFeatures::kPAuthEnhancedPAC2, |
| 489 | CPUFeatures::kPAuthFPAC, |
| 490 | CPUFeatures::kPAuthFPACCombined); |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 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 | |
Jacob Bramley | 8c4ceb6 | 2020-07-08 20:53:32 +0100 | [diff] [blame] | 503 | { |
| 504 | // Scopes can be initialised with no features. |
| 505 | CPUFeaturesScope scope(&top_level); |
| 506 | } |
| 507 | |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 508 | // Check for equivalence. |
| 509 | VIXL_CHECK(cpu.Has(original_outer)); |
| 510 | VIXL_CHECK(original_outer.Has(cpu)); |
| 511 | } |
| 512 | |
Jacob Bramley | 4893432 | 2019-02-04 18:27:53 +0000 | [diff] [blame] | 513 | TEST(CPUFeatures_infer_from_os) { |
| 514 | // Test that CPUFeatures::InferFromOS functions on supported platforms. |
| 515 | CPUFeatures os; |
Jacob Bramley | 2fb52cb | 2019-04-16 15:22:31 +0100 | [diff] [blame] | 516 | VIXL_ASSERT(os.HasNoFeatures()); |
Jacob Bramley | 4893432 | 2019-02-04 18:27:53 +0000 | [diff] [blame] | 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. |
Jacob Bramley | 2fb52cb | 2019-04-16 15:22:31 +0100 | [diff] [blame] | 521 | if (os.HasNoFeatures()) { |
Jacob Bramley | 4893432 | 2019-02-04 18:27:53 +0000 | [diff] [blame] | 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 | |
| 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 { |
Anthony Barbier | 88e1d03 | 2019-06-13 15:20:20 +0100 | [diff] [blame] | 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"; |
Jacob Bramley | 4893432 | 2019-02-04 18:27:53 +0000 | [diff] [blame] | 559 | } |
| 560 | } |
| 561 | |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 562 | } // namespace vixl |