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 | |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 32 | #include "test-runner.h" |
| 33 | |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 34 | #include "cpu-features.h" |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 35 | #include "utils-vixl.h" |
| 36 | |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 37 | #if __cplusplus >= 201103L |
| 38 | #include <type_traits> |
| 39 | #endif |
| 40 | |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 41 | #define TEST(name) TEST_(API_##name) |
| 42 | |
| 43 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) |
| 44 | |
| 45 | namespace vixl { |
| 46 | |
| 47 | // Describe the result of a test. Should IsUintN() and IsIntN() return true or |
| 48 | // false for N and X? |
| 49 | template <typename T> |
| 50 | struct UintIntTest { |
| 51 | bool is_uintn; |
| 52 | bool is_intn; |
| 53 | unsigned n; |
| 54 | T x; |
| 55 | }; |
| 56 | |
| 57 | // Test IsUintN() and IsIntN() against various values and integral types. |
| 58 | TEST(IsUint_IsInt) { |
| 59 | UintIntTest<uint32_t> test_little_values_unsigned[] = { |
Pierre Langlois | 1bce007 | 2017-06-06 17:58:58 +0100 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 68 | |
Pierre Langlois | 1bce007 | 2017-06-06 17:58:58 +0100 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 77 | }; |
| 78 | |
| 79 | UintIntTest<int32_t> test_little_values_signed[] = { |
Pierre Langlois | 1bce007 | 2017-06-06 17:58:58 +0100 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 88 | |
Pierre Langlois | 1bce007 | 2017-06-06 17:58:58 +0100 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 97 | }; |
| 98 | |
| 99 | UintIntTest<uint32_t> test_u16[] = { |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 108 | }; |
| 109 | |
| 110 | UintIntTest<int32_t> test_i16[] = { |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 119 | }; |
| 120 | |
| 121 | UintIntTest<uint64_t> test_u32[] = { |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 126 | }; |
| 127 | |
| 128 | UintIntTest<int64_t> test_i32[] = { |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 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)) |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 135 | }; |
| 136 | |
| 137 | UintIntTest<uint64_t> test_unsigned_higher_than_32[] = { |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 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)}, |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 142 | }; |
| 143 | |
| 144 | UintIntTest<int64_t> test_signed_higher_than_32[] = { |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 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)) |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 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 | |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 162 | #define TEST_UINT(test_vector) \ |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 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 | |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 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 | } \ |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 178 | } |
| 179 | |
Pierre Langlois | bde2e4b | 2017-01-24 17:41:26 +0000 | [diff] [blame] | 180 | TEST_LIST(TEST_UINT) |
| 181 | TEST_LIST(TEST_INT) |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 182 | |
| 183 | #undef TEST_UINT |
| 184 | #undef TEST_INT |
| 185 | |
| 186 | #undef TEST_LIST |
| 187 | } |
| 188 | |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 189 | |
| 190 | TEST(CPUFeatures_iterator_api) { |
| 191 | // CPUFeaturesIterator does not fully satisfy the requirements of C++'s |
| 192 | // iterator concepts, but it should implement enough for some basic usage. |
| 193 | |
| 194 | // Arbitrary feature lists. |
| 195 | CPUFeatures f1(CPUFeatures::kFP, CPUFeatures::kNEON); |
| 196 | CPUFeatures f2(CPUFeatures::kFP, CPUFeatures::kNEON, CPUFeatures::kCRC32); |
| 197 | CPUFeatures f3; |
| 198 | |
| 199 | typedef CPUFeatures::const_iterator It; |
| 200 | |
| 201 | It it0; |
| 202 | It it1_neon(&f1, CPUFeatures::kNEON); |
| 203 | It it2_neon(&f2, CPUFeatures::kNEON); |
| 204 | It it2_crc32(&f2, CPUFeatures::kCRC32); |
| 205 | It it3(&f3); |
| 206 | |
| 207 | // Equality |
| 208 | VIXL_CHECK(it0 == it0); |
| 209 | VIXL_CHECK(it1_neon == it1_neon); |
| 210 | VIXL_CHECK(it2_neon == it2_neon); |
| 211 | VIXL_CHECK(it2_crc32 == it2_crc32); |
| 212 | VIXL_CHECK(it3 == it3); |
| 213 | VIXL_CHECK(!(it0 == it1_neon)); |
| 214 | VIXL_CHECK(!(it0 == it3)); |
| 215 | VIXL_CHECK(!(it1_neon == it2_neon)); |
| 216 | VIXL_CHECK(!(it1_neon == it2_crc32)); |
| 217 | VIXL_CHECK(!(it1_neon == it3)); |
| 218 | VIXL_CHECK(!(it2_neon == it2_crc32)); |
| 219 | VIXL_CHECK(!(it3 == it0)); |
| 220 | VIXL_CHECK(!(it3 == it1_neon)); |
| 221 | |
| 222 | // Inequality |
| 223 | // (a == b) <-> !(a != b) |
| 224 | VIXL_CHECK(!(it0 != it0)); |
| 225 | VIXL_CHECK(!(it1_neon != it1_neon)); |
| 226 | VIXL_CHECK(!(it2_neon != it2_neon)); |
| 227 | VIXL_CHECK(!(it2_crc32 != it2_crc32)); |
| 228 | VIXL_CHECK(!(it3 != it3)); |
| 229 | // !(a == b) <-> (a != b) |
| 230 | VIXL_CHECK(it0 != it1_neon); |
| 231 | VIXL_CHECK(it0 != it3); |
| 232 | VIXL_CHECK(it1_neon != it2_neon); |
| 233 | VIXL_CHECK(it1_neon != it2_crc32); |
| 234 | VIXL_CHECK(it1_neon != it3); |
| 235 | VIXL_CHECK(it2_neon != it2_crc32); |
| 236 | VIXL_CHECK(it3 != it0); |
| 237 | VIXL_CHECK(it3 != it1_neon); |
| 238 | |
| 239 | // Dereferenceable |
| 240 | VIXL_CHECK(*it0 == CPUFeatures::kNone); |
| 241 | VIXL_CHECK(*it1_neon == CPUFeatures::kNEON); |
| 242 | VIXL_CHECK(*it2_neon == CPUFeatures::kNEON); |
| 243 | VIXL_CHECK(*it2_crc32 == CPUFeatures::kCRC32); |
| 244 | VIXL_CHECK(*it3 == CPUFeatures::kNone); |
| 245 | |
| 246 | #if __cplusplus >= 201103L |
| 247 | VIXL_STATIC_ASSERT(std::is_copy_constructible<It>::value); |
| 248 | VIXL_STATIC_ASSERT(std::is_copy_assignable<It>::value); |
| 249 | VIXL_STATIC_ASSERT(std::is_destructible<It>::value); |
| 250 | #endif |
| 251 | // Copy constructable |
| 252 | It test0 = it0; |
| 253 | It test1 = it1_neon; |
| 254 | It test2(it2_neon); |
| 255 | VIXL_CHECK(test0 == It(NULL, CPUFeatures::kNone)); |
| 256 | VIXL_CHECK(test1 == It(&f1, CPUFeatures::kNEON)); |
| 257 | VIXL_CHECK(test2 == It(&f2, CPUFeatures::kNEON)); |
| 258 | |
| 259 | // Copy assignable |
| 260 | test2 = it2_crc32; |
| 261 | VIXL_CHECK(test2 == It(&f2, CPUFeatures::kCRC32)); |
| 262 | |
| 263 | // Incrementable |
| 264 | // - Incrementing has no effect on an empty CPUFeatures. |
| 265 | VIXL_CHECK(it3++ == CPUFeatures::kNone); |
| 266 | VIXL_CHECK(++it3 == CPUFeatures::kNone); |
| 267 | VIXL_CHECK(it3 == It(&f3, CPUFeatures::kNone)); |
| 268 | // - Incrementing moves to the next feature, wrapping around (through kNone). |
| 269 | // This test will need to be updated if the Feature enum is reordered. |
| 270 | VIXL_CHECK(it2_neon++ == CPUFeatures::kNEON); |
| 271 | VIXL_CHECK(it2_neon++ == CPUFeatures::kCRC32); |
| 272 | VIXL_CHECK(it2_neon++ == CPUFeatures::kNone); |
| 273 | VIXL_CHECK(it2_neon++ == CPUFeatures::kFP); |
| 274 | VIXL_CHECK(it2_neon == It(&f2, CPUFeatures::kNEON)); |
| 275 | VIXL_CHECK(++it2_crc32 == CPUFeatures::kNone); |
| 276 | VIXL_CHECK(++it2_crc32 == CPUFeatures::kFP); |
| 277 | VIXL_CHECK(++it2_crc32 == CPUFeatures::kNEON); |
| 278 | VIXL_CHECK(++it2_crc32 == CPUFeatures::kCRC32); |
| 279 | VIXL_CHECK(it2_crc32 == It(&f2, CPUFeatures::kCRC32)); |
| 280 | } |
| 281 | |
| 282 | |
| 283 | TEST(CPUFeatures_iterator_loops) { |
| 284 | // Check that CPUFeaturesIterator can be used for some simple loops. |
| 285 | |
| 286 | // Arbitrary feature lists. |
| 287 | CPUFeatures f1(CPUFeatures::kFP, CPUFeatures::kNEON); |
| 288 | CPUFeatures f2(CPUFeatures::kFP, CPUFeatures::kNEON, CPUFeatures::kCRC32); |
| 289 | CPUFeatures f3; |
| 290 | |
| 291 | // This test will need to be updated if the Feature enum is reordered. |
| 292 | |
| 293 | std::vector<CPUFeatures::Feature> f1_list; |
| 294 | for (CPUFeatures::const_iterator it = f1.begin(); it != f1.end(); ++it) { |
| 295 | f1_list.push_back(*it); |
| 296 | } |
| 297 | VIXL_CHECK(f1_list.size() == 2); |
| 298 | VIXL_CHECK(f1_list[0] == CPUFeatures::kFP); |
| 299 | VIXL_CHECK(f1_list[1] == CPUFeatures::kNEON); |
| 300 | |
| 301 | std::vector<CPUFeatures::Feature> f2_list; |
| 302 | for (CPUFeatures::const_iterator it = f2.begin(); it != f2.end(); ++it) { |
| 303 | f2_list.push_back(*it); |
| 304 | } |
| 305 | VIXL_CHECK(f2_list.size() == 3); |
| 306 | VIXL_CHECK(f2_list[0] == CPUFeatures::kFP); |
| 307 | VIXL_CHECK(f2_list[1] == CPUFeatures::kNEON); |
| 308 | VIXL_CHECK(f2_list[2] == CPUFeatures::kCRC32); |
| 309 | |
| 310 | std::vector<CPUFeatures::Feature> f3_list; |
| 311 | for (CPUFeatures::const_iterator it = f3.begin(); it != f3.end(); ++it) { |
| 312 | f3_list.push_back(*it); |
| 313 | } |
| 314 | VIXL_CHECK(f3_list.size() == 0); |
| 315 | |
| 316 | #if __cplusplus >= 201103L |
| 317 | std::vector<CPUFeatures::Feature> f2_list_cxx11; |
| 318 | for (auto&& feature : f2) { |
| 319 | f2_list_cxx11.push_back(feature); |
| 320 | } |
| 321 | VIXL_CHECK(f2_list_cxx11.size() == 3); |
| 322 | VIXL_CHECK(f2_list_cxx11[0] == CPUFeatures::kFP); |
| 323 | VIXL_CHECK(f2_list_cxx11[1] == CPUFeatures::kNEON); |
| 324 | VIXL_CHECK(f2_list_cxx11[2] == CPUFeatures::kCRC32); |
| 325 | |
| 326 | std::vector<CPUFeatures::Feature> f3_list_cxx11; |
| 327 | for (auto&& feature : f3) { |
| 328 | f3_list_cxx11.push_back(feature); |
| 329 | } |
| 330 | VIXL_CHECK(f3_list_cxx11.size() == 0); |
| 331 | #endif |
| 332 | } |
| 333 | |
| 334 | |
| 335 | TEST(CPUFeatures_empty) { |
| 336 | // A default-constructed CPUFeatures has no features enabled. |
| 337 | CPUFeatures f; |
| 338 | for (CPUFeatures::const_iterator it = f.begin(); it != f.end(); ++it) { |
| 339 | VIXL_ABORT(); |
| 340 | } |
| 341 | } |
| 342 | |
| 343 | |
| 344 | static void CPUFeaturesFormatHelper(const char* expected, |
| 345 | const CPUFeatures& features) { |
| 346 | std::stringstream os; |
| 347 | os << features; |
| 348 | std::string os_str = os.str(); |
| 349 | if (os_str != expected) { |
| 350 | std::cout << "Found: " << os_str << "\n"; |
| 351 | std::cout << "Expected: " << expected << "\n"; |
| 352 | VIXL_ABORT(); |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | |
| 357 | TEST(CPUFeatures_format) { |
| 358 | // Check that the debug output is complete and accurate. |
| 359 | |
| 360 | // Individual features. |
| 361 | CPUFeaturesFormatHelper("", CPUFeatures(CPUFeatures::kNone)); |
| 362 | CPUFeaturesFormatHelper("FP", CPUFeatures(CPUFeatures::kFP)); |
| 363 | CPUFeaturesFormatHelper("NEON", CPUFeatures(CPUFeatures::kNEON)); |
| 364 | CPUFeaturesFormatHelper("AES", CPUFeatures(CPUFeatures::kAES)); |
| 365 | CPUFeaturesFormatHelper("Pmull1Q", CPUFeatures(CPUFeatures::kPmull1Q)); |
| 366 | CPUFeaturesFormatHelper("SHA1", CPUFeatures(CPUFeatures::kSHA1)); |
| 367 | CPUFeaturesFormatHelper("SHA2", CPUFeatures(CPUFeatures::kSHA2)); |
| 368 | CPUFeaturesFormatHelper("CRC32", CPUFeatures(CPUFeatures::kCRC32)); |
| 369 | |
| 370 | // Combinations of (arbitrary) features. |
| 371 | // This test will need to be updated if the Feature enum is reordered. |
| 372 | CPUFeatures f(CPUFeatures::kFP, CPUFeatures::kNEON); |
| 373 | CPUFeaturesFormatHelper("FP, NEON", f); |
| 374 | f.Combine(CPUFeatures::kCRC32); |
| 375 | CPUFeaturesFormatHelper("FP, NEON, CRC32", f); |
| 376 | f.Combine(CPUFeatures::kFcma); |
| 377 | CPUFeaturesFormatHelper("FP, NEON, CRC32, Fcma", f); |
| 378 | f.Combine(CPUFeatures::kSHA1); |
| 379 | CPUFeaturesFormatHelper("FP, NEON, CRC32, SHA1, Fcma", f); |
| 380 | |
| 381 | CPUFeaturesFormatHelper( |
| 382 | "ID register emulation, " |
| 383 | // Armv8.0 |
| 384 | "FP, NEON, CRC32, " |
| 385 | "AES, SHA1, SHA2, Pmull1Q, " |
| 386 | // Armv8.1 |
| 387 | "Atomics, LORegions, RDM, " |
| 388 | // Armv8.2 |
TatWai Chong | 684f5f7 | 2018-12-25 17:49:56 -0800 | [diff] [blame] | 389 | "SVE, DotProduct, FPHalf, NEONHalf, RAS, DCPoP, DCCVADP, SHA3, SHA512, " |
| 390 | "SM3, SM4, " |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 391 | // Armv8.3 |
Jacob Bramley | 4482be7 | 2018-09-14 15:57:57 +0100 | [diff] [blame] | 392 | "PAuth, PAuthQARMA, PAuthGeneric, PAuthGenericQARMA, JSCVT, Fcma, RCpc, " |
| 393 | // Armv8.4 |
Martyn Capewell | cb963f7 | 2018-10-22 15:25:28 +0100 | [diff] [blame] | 394 | "RCpc (imm), FlagM, USCAT, FHM, DIT, " |
| 395 | // Armv8.5 |
TatWai Chong | 04edf68 | 2018-12-27 16:01:02 -0800 | [diff] [blame^] | 396 | "BTI, AXFlag, RNG", |
Jacob Bramley | 2af191d | 2018-05-16 10:22:44 +0100 | [diff] [blame] | 397 | CPUFeatures::All()); |
| 398 | } |
| 399 | |
| 400 | |
| 401 | static void CPUFeaturesPredefinedResultCheckHelper( |
| 402 | const std::set<CPUFeatures::Feature>& unexpected, |
| 403 | const std::set<CPUFeatures::Feature>& expected) { |
| 404 | // Print a helpful diagnostic before checking the result. |
| 405 | typedef std::set<CPUFeatures::Feature>::const_iterator It; |
| 406 | if (!unexpected.empty()) { |
| 407 | std::cout << "Unexpected features:\n"; |
| 408 | for (It it = unexpected.begin(); it != unexpected.end(); ++it) { |
| 409 | std::cout << " " << *it << "\n"; |
| 410 | } |
| 411 | } |
| 412 | if (!expected.empty()) { |
| 413 | std::cout << "Missing features:\n"; |
| 414 | for (It it = expected.begin(); it != expected.end(); ++it) { |
| 415 | std::cout << " " << *it << "\n"; |
| 416 | } |
| 417 | } |
| 418 | VIXL_CHECK(unexpected.empty() && expected.empty()); |
| 419 | } |
| 420 | |
| 421 | |
| 422 | TEST(CPUFeatures_predefined_legacy) { |
| 423 | CPUFeatures f = CPUFeatures::AArch64LegacyBaseline(); |
| 424 | std::set<CPUFeatures::Feature> unexpected; |
| 425 | std::set<CPUFeatures::Feature> expected; |
| 426 | expected.insert(CPUFeatures::kFP); |
| 427 | expected.insert(CPUFeatures::kNEON); |
| 428 | expected.insert(CPUFeatures::kCRC32); |
| 429 | |
| 430 | for (CPUFeatures::const_iterator it = f.begin(); it != f.end(); ++it) { |
| 431 | if (expected.erase(*it) == 0) unexpected.insert(*it); |
| 432 | } |
| 433 | CPUFeaturesPredefinedResultCheckHelper(unexpected, expected); |
| 434 | } |
| 435 | |
| 436 | |
| 437 | TEST(CPUFeatures_predefined_all) { |
| 438 | CPUFeatures f = CPUFeatures::All(); |
| 439 | std::set<CPUFeatures::Feature> found; |
| 440 | |
| 441 | for (CPUFeatures::const_iterator it = f.begin(); it != f.end(); ++it) { |
| 442 | found.insert(*it); |
| 443 | } |
| 444 | VIXL_CHECK(found.size() == CPUFeatures::kNumberOfFeatures); |
| 445 | } |
| 446 | |
| 447 | // The CPUFeaturesScope constructor is templated, and needs an object which |
| 448 | // implements `CPUFeatures* GetCPUFeatures()`. This is normally something like |
| 449 | // the Assembler, but for the tests we use an architecture-independent wrapper. |
| 450 | class GetCPUFeaturesWrapper { |
| 451 | public: |
| 452 | explicit GetCPUFeaturesWrapper(CPUFeatures* cpu_features) |
| 453 | : cpu_features_(cpu_features) {} |
| 454 | |
| 455 | CPUFeatures* GetCPUFeatures() const { return cpu_features_; } |
| 456 | |
| 457 | private: |
| 458 | CPUFeatures* cpu_features_; |
| 459 | }; |
| 460 | |
| 461 | TEST(CPUFeaturesScope) { |
| 462 | // Test that CPUFeaturesScope properly preserves state. |
| 463 | |
| 464 | CPUFeatures cpu(CPUFeatures::kCRC32, CPUFeatures::kSHA1, CPUFeatures::kAES); |
| 465 | GetCPUFeaturesWrapper top_level(&cpu); |
| 466 | |
| 467 | const CPUFeatures original_outer = cpu; |
| 468 | |
| 469 | { // Test setting both new and existing features. |
| 470 | CPUFeaturesScope outer(&top_level, CPUFeatures::kSHA2, CPUFeatures::kAES); |
| 471 | VIXL_CHECK(outer.GetCPUFeatures() == &cpu); |
| 472 | VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32, |
| 473 | CPUFeatures::kSHA1, |
| 474 | CPUFeatures::kSHA2, |
| 475 | CPUFeatures::kAES)); |
| 476 | |
| 477 | // Features can be added or removed directly, in the usual fashion. |
| 478 | // (The scope will restore their original status when it ends.) |
| 479 | cpu.Combine(CPUFeatures::kSHA1, CPUFeatures::kAtomics); |
| 480 | VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32, |
| 481 | CPUFeatures::kSHA1, |
| 482 | CPUFeatures::kSHA2, |
| 483 | CPUFeatures::kAES)); |
| 484 | VIXL_CHECK(cpu.Has(CPUFeatures::kAtomics)); |
| 485 | |
| 486 | cpu.Remove(CPUFeatures::kSHA2, CPUFeatures::kAES); |
| 487 | VIXL_CHECK(!cpu.Has(CPUFeatures::kSHA2, CPUFeatures::kAES)); |
| 488 | VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32, |
| 489 | CPUFeatures::kSHA1, |
| 490 | CPUFeatures::kAtomics)); |
| 491 | |
| 492 | const CPUFeatures original_inner = cpu; |
| 493 | |
| 494 | // Scopes can be nested. |
| 495 | { |
| 496 | // A CPUFeaturesScope can be constructed from a CPUFeatures*, or any |
| 497 | // (non-local) object that implements `CPUFeatures* GetCPUFeatures()`. |
| 498 | // Typically, this would be an Assembler or MacroAssembler, but |
| 499 | // CPUFeatureScope itself provides this method, and allows the test to |
| 500 | // remain architecture-agnostic. |
| 501 | |
| 502 | CPUFeatures auth(CPUFeatures::kPAuth, |
| 503 | CPUFeatures::kPAuthQARMA, |
| 504 | CPUFeatures::kPAuthGeneric, |
| 505 | CPUFeatures::kPAuthGenericQARMA); |
| 506 | |
| 507 | CPUFeaturesScope inner(&outer, auth); |
| 508 | VIXL_CHECK(inner.GetCPUFeatures() == &cpu); |
| 509 | VIXL_CHECK(cpu.Has(auth.With(CPUFeatures::kCRC32, |
| 510 | CPUFeatures::kSHA1, |
| 511 | CPUFeatures::kAtomics))); |
| 512 | } |
| 513 | // Check for equivalence. |
| 514 | VIXL_CHECK(cpu.Has(original_inner)); |
| 515 | VIXL_CHECK(original_inner.Has(cpu)); |
| 516 | } |
| 517 | |
| 518 | // Check for equivalence. |
| 519 | VIXL_CHECK(cpu.Has(original_outer)); |
| 520 | VIXL_CHECK(original_outer.Has(cpu)); |
| 521 | } |
| 522 | |
Pierre Langlois | efe0c1f | 2016-11-24 11:54:47 +0000 | [diff] [blame] | 523 | } // namespace vixl |