| // Copyright 2016, VIXL authors |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // * Redistributions of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // * Neither the name of ARM Limited nor the names of its contributors may be |
| // used to endorse or promote products derived from this software without |
| // specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND |
| // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include <iostream> |
| #include <set> |
| #include <sstream> |
| #include <vector> |
| |
| #include "test-runner.h" |
| |
| #include "cpu-features.h" |
| #include "utils-vixl.h" |
| |
| #if __cplusplus >= 201103L |
| #include <type_traits> |
| #endif |
| |
| #define TEST(name) TEST_(API_##name) |
| |
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) |
| |
| namespace vixl { |
| |
| // Describe the result of a test. Should IsUintN() and IsIntN() return true or |
| // false for N and X? |
| template <typename T> |
| struct UintIntTest { |
| bool is_uintn; |
| bool is_intn; |
| unsigned n; |
| T x; |
| }; |
| |
| // Test IsUintN() and IsIntN() against various values and integral types. |
| TEST(IsUint_IsInt) { |
| UintIntTest<uint32_t> test_little_values_unsigned[] = { |
| {true, true, 1, UINT32_C(0x0)}, {true, false, 1, UINT32_C(0x1)}, |
| {false, false, 1, UINT32_C(0x2)}, {false, false, 1, UINT32_C(0x3)}, |
| {false, false, 1, UINT32_C(0x4)}, {false, false, 1, UINT32_C(0x5)}, |
| {false, false, 1, UINT32_C(0x6)}, {false, false, 1, UINT32_C(0x7)}, |
| {false, false, 1, UINT32_C(0x8)}, {false, false, 1, UINT32_C(0x9)}, |
| {false, false, 1, UINT32_C(0xa)}, {false, false, 1, UINT32_C(0xb)}, |
| {false, false, 1, UINT32_C(0xc)}, {false, false, 1, UINT32_C(0xd)}, |
| {false, false, 1, UINT32_C(0xe)}, {false, false, 1, UINT32_C(0xf)}, |
| |
| {true, true, 2, UINT32_C(0x0)}, {true, true, 2, UINT32_C(0x1)}, |
| {true, false, 2, UINT32_C(0x2)}, {true, false, 2, UINT32_C(0x3)}, |
| {false, false, 2, UINT32_C(0x4)}, {false, false, 2, UINT32_C(0x5)}, |
| {false, false, 2, UINT32_C(0x6)}, {false, false, 2, UINT32_C(0x7)}, |
| {false, false, 2, UINT32_C(0x8)}, {false, false, 2, UINT32_C(0x9)}, |
| {false, false, 2, UINT32_C(0xa)}, {false, false, 2, UINT32_C(0xb)}, |
| {false, false, 2, UINT32_C(0xc)}, {false, false, 2, UINT32_C(0xd)}, |
| {false, false, 2, UINT32_C(0xe)}, {false, false, 2, UINT32_C(0xf)}, |
| }; |
| |
| UintIntTest<int32_t> test_little_values_signed[] = { |
| {true, true, 1, INT32_C(0)}, {true, false, 1, INT32_C(1)}, |
| {false, false, 1, INT32_C(2)}, {false, false, 1, INT32_C(3)}, |
| {false, false, 1, INT32_C(4)}, {false, false, 1, INT32_C(5)}, |
| {false, false, 1, INT32_C(6)}, {false, false, 1, INT32_C(7)}, |
| {false, true, 1, INT32_C(-1)}, {false, false, 1, INT32_C(-2)}, |
| {false, false, 1, INT32_C(-3)}, {false, false, 1, INT32_C(-4)}, |
| {false, false, 1, INT32_C(-5)}, {false, false, 1, INT32_C(-6)}, |
| {false, false, 1, INT32_C(-7)}, {false, false, 1, INT32_C(-8)}, |
| |
| {true, true, 2, INT32_C(0)}, {true, true, 2, INT32_C(1)}, |
| {true, false, 2, INT32_C(2)}, {true, false, 2, INT32_C(3)}, |
| {false, false, 2, INT32_C(4)}, {false, false, 2, INT32_C(5)}, |
| {false, false, 2, INT32_C(6)}, {false, false, 2, INT32_C(7)}, |
| {false, true, 2, INT32_C(-1)}, {false, true, 2, INT32_C(-2)}, |
| {false, false, 2, INT32_C(-3)}, {false, false, 2, INT32_C(-4)}, |
| {false, false, 2, INT32_C(-5)}, {false, false, 2, INT32_C(-6)}, |
| {false, false, 2, INT32_C(-7)}, {false, false, 2, INT32_C(-8)}, |
| }; |
| |
| UintIntTest<uint32_t> test_u16[] = { |
| {true, true, 16, UINT32_C(0x0)}, |
| {true, false, 16, UINT32_C(0xabcd)}, |
| {true, false, 16, UINT32_C(0x8000)}, |
| {true, false, 16, UINT32_C(0xffff)}, |
| {false, false, 16, UINT32_C(0x10000)}, |
| {false, false, 16, UINT32_C(0xffff0000)}, |
| {false, false, 16, UINT32_C(0xffff8000)}, |
| {false, false, 16, UINT32_C(0xffffffff)}, |
| }; |
| |
| UintIntTest<int32_t> test_i16[] = { |
| {true, true, 16, INT32_C(0x0)}, |
| {true, false, 16, INT32_C(0xabcd)}, |
| {true, false, 16, INT32_C(0x8000)}, |
| {true, false, 16, INT32_C(0xffff)}, |
| {false, false, 16, INT32_C(0x10000)}, |
| {true, true, 16, INT32_C(42)}, |
| {false, true, 16, INT32_C(-42)}, |
| {false, true, 16, INT32_C(-1)}, |
| }; |
| |
| UintIntTest<uint64_t> test_u32[] = { |
| {true, true, 32, UINT64_C(0x0)}, |
| {true, false, 32, UINT64_C(0xabcdabcd)}, |
| {true, false, 32, UINT64_C(0x80000000)}, |
| {true, false, 32, UINT64_C(0xffffffff)}, |
| }; |
| |
| UintIntTest<int64_t> test_i32[] = { |
| {true, true, 32, INT64_C(0)}, |
| {true, true, 32, INT64_C(42)}, |
| {false, true, 32, INT64_C(-42)}, |
| {false, true, 32, INT64_C(-1)}, |
| {true, true, 32, INT64_C(2147483647)}, // (1 << (32 - 1)) - 1 |
| {false, true, 32, INT64_C(-2147483648)}, // -(1 << (32 - 1)) |
| }; |
| |
| UintIntTest<uint64_t> test_unsigned_higher_than_32[] = { |
| {false, false, 54, UINT64_C(0xabcdef9012345678)}, |
| {true, false, 33, UINT64_C(0x100000000)}, |
| {true, false, 62, UINT64_C(0x3fffffffffffffff)}, |
| {true, false, 63, UINT64_C(0x7fffffffffffffff)}, |
| }; |
| |
| UintIntTest<int64_t> test_signed_higher_than_32[] = { |
| {true, true, 54, INT64_C(9007199254740991)}, // (1 << (54 - 1)) - 1 |
| {true, false, 54, INT64_C(9007199254740992)}, // 1 << (54 - 1) |
| {true, true, 33, INT64_C(4294967295)}, // (1 << (33 - 1) - 1) |
| {false, true, 33, INT64_C(-4294967296)}, // -(1 << (33 - 1)) |
| }; |
| |
| #define TEST_LIST(M) \ |
| M(test_little_values_unsigned) \ |
| M(test_little_values_signed) \ |
| M(test_u16) \ |
| M(test_i16) \ |
| M(test_u32) \ |
| M(test_i32) \ |
| M(test_unsigned_higher_than_32) \ |
| M(test_signed_higher_than_32) |
| |
| |
| #define TEST_UINT(test_vector) \ |
| for (unsigned i = 0; i < ARRAY_SIZE(test_vector); i++) { \ |
| if (test_vector[i].is_uintn) { \ |
| VIXL_CHECK(IsUintN(test_vector[i].n, test_vector[i].x)); \ |
| } else { \ |
| VIXL_CHECK(!IsUintN(test_vector[i].n, test_vector[i].x)); \ |
| } \ |
| } |
| |
| #define TEST_INT(test_vector) \ |
| for (unsigned i = 0; i < ARRAY_SIZE(test_vector); i++) { \ |
| if (test_vector[i].is_intn) { \ |
| VIXL_CHECK(IsIntN(test_vector[i].n, test_vector[i].x)); \ |
| } else { \ |
| VIXL_CHECK(!IsIntN(test_vector[i].n, test_vector[i].x)); \ |
| } \ |
| } |
| |
| TEST_LIST(TEST_UINT) |
| TEST_LIST(TEST_INT) |
| |
| #undef TEST_UINT |
| #undef TEST_INT |
| |
| #undef TEST_LIST |
| } |
| |
| |
| TEST(CPUFeatures_iterator_api) { |
| // CPUFeaturesIterator does not fully satisfy the requirements of C++'s |
| // iterator concepts, but it should implement enough for some basic usage. |
| |
| // Arbitrary feature lists. |
| CPUFeatures f1(CPUFeatures::kFP, CPUFeatures::kNEON); |
| CPUFeatures f2(CPUFeatures::kFP, CPUFeatures::kNEON, CPUFeatures::kCRC32); |
| CPUFeatures f3; |
| |
| typedef CPUFeatures::const_iterator It; |
| |
| It it0; |
| It it1_neon(&f1, CPUFeatures::kNEON); |
| It it2_neon(&f2, CPUFeatures::kNEON); |
| It it2_crc32(&f2, CPUFeatures::kCRC32); |
| It it3(&f3); |
| |
| // Equality |
| VIXL_CHECK(it0 == it0); |
| VIXL_CHECK(it1_neon == it1_neon); |
| VIXL_CHECK(it2_neon == it2_neon); |
| VIXL_CHECK(it2_crc32 == it2_crc32); |
| VIXL_CHECK(it3 == it3); |
| VIXL_CHECK(!(it0 == it1_neon)); |
| VIXL_CHECK(!(it0 == it3)); |
| VIXL_CHECK(!(it1_neon == it2_neon)); |
| VIXL_CHECK(!(it1_neon == it2_crc32)); |
| VIXL_CHECK(!(it1_neon == it3)); |
| VIXL_CHECK(!(it2_neon == it2_crc32)); |
| VIXL_CHECK(!(it3 == it0)); |
| VIXL_CHECK(!(it3 == it1_neon)); |
| |
| // Inequality |
| // (a == b) <-> !(a != b) |
| VIXL_CHECK(!(it0 != it0)); |
| VIXL_CHECK(!(it1_neon != it1_neon)); |
| VIXL_CHECK(!(it2_neon != it2_neon)); |
| VIXL_CHECK(!(it2_crc32 != it2_crc32)); |
| VIXL_CHECK(!(it3 != it3)); |
| // !(a == b) <-> (a != b) |
| VIXL_CHECK(it0 != it1_neon); |
| VIXL_CHECK(it0 != it3); |
| VIXL_CHECK(it1_neon != it2_neon); |
| VIXL_CHECK(it1_neon != it2_crc32); |
| VIXL_CHECK(it1_neon != it3); |
| VIXL_CHECK(it2_neon != it2_crc32); |
| VIXL_CHECK(it3 != it0); |
| VIXL_CHECK(it3 != it1_neon); |
| |
| // Dereferenceable |
| VIXL_CHECK(*it0 == CPUFeatures::kNone); |
| VIXL_CHECK(*it1_neon == CPUFeatures::kNEON); |
| VIXL_CHECK(*it2_neon == CPUFeatures::kNEON); |
| VIXL_CHECK(*it2_crc32 == CPUFeatures::kCRC32); |
| VIXL_CHECK(*it3 == CPUFeatures::kNone); |
| |
| #if __cplusplus >= 201103L |
| VIXL_STATIC_ASSERT(std::is_copy_constructible<It>::value); |
| VIXL_STATIC_ASSERT(std::is_copy_assignable<It>::value); |
| VIXL_STATIC_ASSERT(std::is_destructible<It>::value); |
| #endif |
| // Copy constructable |
| It test0 = it0; |
| It test1 = it1_neon; |
| It test2(it2_neon); |
| VIXL_CHECK(test0 == It(NULL, CPUFeatures::kNone)); |
| VIXL_CHECK(test1 == It(&f1, CPUFeatures::kNEON)); |
| VIXL_CHECK(test2 == It(&f2, CPUFeatures::kNEON)); |
| |
| // Copy assignable |
| test2 = it2_crc32; |
| VIXL_CHECK(test2 == It(&f2, CPUFeatures::kCRC32)); |
| |
| // Incrementable |
| // - Incrementing has no effect on an empty CPUFeatures. |
| VIXL_CHECK(it3++ == CPUFeatures::kNone); |
| VIXL_CHECK(++it3 == CPUFeatures::kNone); |
| VIXL_CHECK(it3 == It(&f3, CPUFeatures::kNone)); |
| // - Incrementing moves to the next feature, wrapping around (through kNone). |
| // This test will need to be updated if the Feature enum is reordered. |
| VIXL_CHECK(it2_neon++ == CPUFeatures::kNEON); |
| VIXL_CHECK(it2_neon++ == CPUFeatures::kCRC32); |
| VIXL_CHECK(it2_neon++ == CPUFeatures::kNone); |
| VIXL_CHECK(it2_neon++ == CPUFeatures::kFP); |
| VIXL_CHECK(it2_neon == It(&f2, CPUFeatures::kNEON)); |
| VIXL_CHECK(++it2_crc32 == CPUFeatures::kNone); |
| VIXL_CHECK(++it2_crc32 == CPUFeatures::kFP); |
| VIXL_CHECK(++it2_crc32 == CPUFeatures::kNEON); |
| VIXL_CHECK(++it2_crc32 == CPUFeatures::kCRC32); |
| VIXL_CHECK(it2_crc32 == It(&f2, CPUFeatures::kCRC32)); |
| } |
| |
| |
| TEST(CPUFeatures_iterator_loops) { |
| // Check that CPUFeaturesIterator can be used for some simple loops. |
| |
| // Arbitrary feature lists. |
| CPUFeatures f1(CPUFeatures::kFP, CPUFeatures::kNEON); |
| CPUFeatures f2(CPUFeatures::kFP, CPUFeatures::kNEON, CPUFeatures::kCRC32); |
| CPUFeatures f3; |
| |
| // This test will need to be updated if the Feature enum is reordered. |
| |
| std::vector<CPUFeatures::Feature> f1_list; |
| for (CPUFeatures::const_iterator it = f1.begin(); it != f1.end(); ++it) { |
| f1_list.push_back(*it); |
| } |
| VIXL_CHECK(f1_list.size() == 2); |
| VIXL_CHECK(f1_list[0] == CPUFeatures::kFP); |
| VIXL_CHECK(f1_list[1] == CPUFeatures::kNEON); |
| |
| std::vector<CPUFeatures::Feature> f2_list; |
| for (CPUFeatures::const_iterator it = f2.begin(); it != f2.end(); ++it) { |
| f2_list.push_back(*it); |
| } |
| VIXL_CHECK(f2_list.size() == 3); |
| VIXL_CHECK(f2_list[0] == CPUFeatures::kFP); |
| VIXL_CHECK(f2_list[1] == CPUFeatures::kNEON); |
| VIXL_CHECK(f2_list[2] == CPUFeatures::kCRC32); |
| |
| std::vector<CPUFeatures::Feature> f3_list; |
| for (CPUFeatures::const_iterator it = f3.begin(); it != f3.end(); ++it) { |
| f3_list.push_back(*it); |
| } |
| VIXL_CHECK(f3_list.size() == 0); |
| |
| #if __cplusplus >= 201103L |
| std::vector<CPUFeatures::Feature> f2_list_cxx11; |
| for (auto&& feature : f2) { |
| f2_list_cxx11.push_back(feature); |
| } |
| VIXL_CHECK(f2_list_cxx11.size() == 3); |
| VIXL_CHECK(f2_list_cxx11[0] == CPUFeatures::kFP); |
| VIXL_CHECK(f2_list_cxx11[1] == CPUFeatures::kNEON); |
| VIXL_CHECK(f2_list_cxx11[2] == CPUFeatures::kCRC32); |
| |
| std::vector<CPUFeatures::Feature> f3_list_cxx11; |
| for (auto&& feature : f3) { |
| f3_list_cxx11.push_back(feature); |
| } |
| VIXL_CHECK(f3_list_cxx11.size() == 0); |
| #endif |
| } |
| |
| |
| TEST(CPUFeatures_empty) { |
| // A default-constructed CPUFeatures has no features enabled. |
| CPUFeatures f; |
| for (CPUFeatures::const_iterator it = f.begin(); it != f.end(); ++it) { |
| VIXL_ABORT(); |
| } |
| } |
| |
| |
| static void CPUFeaturesFormatHelper(const char* expected, |
| const CPUFeatures& features) { |
| std::stringstream os; |
| os << features; |
| std::string os_str = os.str(); |
| if (os_str != expected) { |
| std::cout << "Found: " << os_str << "\n"; |
| std::cout << "Expected: " << expected << "\n"; |
| VIXL_ABORT(); |
| } |
| } |
| |
| |
| TEST(CPUFeatures_format) { |
| // Check that the debug output is complete and accurate. |
| |
| // Individual features. |
| CPUFeaturesFormatHelper("", CPUFeatures(CPUFeatures::kNone)); |
| CPUFeaturesFormatHelper("FP", CPUFeatures(CPUFeatures::kFP)); |
| CPUFeaturesFormatHelper("NEON", CPUFeatures(CPUFeatures::kNEON)); |
| CPUFeaturesFormatHelper("AES", CPUFeatures(CPUFeatures::kAES)); |
| CPUFeaturesFormatHelper("Pmull1Q", CPUFeatures(CPUFeatures::kPmull1Q)); |
| CPUFeaturesFormatHelper("SHA1", CPUFeatures(CPUFeatures::kSHA1)); |
| CPUFeaturesFormatHelper("SHA2", CPUFeatures(CPUFeatures::kSHA2)); |
| CPUFeaturesFormatHelper("CRC32", CPUFeatures(CPUFeatures::kCRC32)); |
| |
| // Combinations of (arbitrary) features. |
| // This test will need to be updated if the Feature enum is reordered. |
| CPUFeatures f(CPUFeatures::kFP, CPUFeatures::kNEON); |
| CPUFeaturesFormatHelper("FP, NEON", f); |
| f.Combine(CPUFeatures::kCRC32); |
| CPUFeaturesFormatHelper("FP, NEON, CRC32", f); |
| f.Combine(CPUFeatures::kFcma); |
| CPUFeaturesFormatHelper("FP, NEON, CRC32, Fcma", f); |
| f.Combine(CPUFeatures::kSHA1); |
| CPUFeaturesFormatHelper("FP, NEON, CRC32, SHA1, Fcma", f); |
| |
| CPUFeaturesFormatHelper( |
| "ID register emulation, " |
| // Armv8.0 |
| "FP, NEON, CRC32, " |
| "AES, SHA1, SHA2, Pmull1Q, " |
| // Armv8.1 |
| "Atomics, LORegions, RDM, " |
| // Armv8.2 |
| "SVE, DotProduct, FPHalf, NEONHalf, RAS, DCPoP, DCCVADP, SHA3, SHA512, " |
| "SM3, SM4, " |
| // Armv8.3 |
| "PAuth, PAuthQARMA, PAuthGeneric, PAuthGenericQARMA, JSCVT, Fcma, RCpc, " |
| // Armv8.4 |
| "RCpc (imm), FlagM, USCAT, FHM, DIT, " |
| // Armv8.5 |
| "BTI, AXFlag, RNG, Frint (bounded)", |
| CPUFeatures::All()); |
| } |
| |
| |
| static void CPUFeaturesPredefinedResultCheckHelper( |
| const std::set<CPUFeatures::Feature>& unexpected, |
| const std::set<CPUFeatures::Feature>& expected) { |
| // Print a helpful diagnostic before checking the result. |
| typedef std::set<CPUFeatures::Feature>::const_iterator It; |
| if (!unexpected.empty()) { |
| std::cout << "Unexpected features:\n"; |
| for (It it = unexpected.begin(); it != unexpected.end(); ++it) { |
| std::cout << " " << *it << "\n"; |
| } |
| } |
| if (!expected.empty()) { |
| std::cout << "Missing features:\n"; |
| for (It it = expected.begin(); it != expected.end(); ++it) { |
| std::cout << " " << *it << "\n"; |
| } |
| } |
| VIXL_CHECK(unexpected.empty() && expected.empty()); |
| } |
| |
| |
| TEST(CPUFeatures_predefined_legacy) { |
| CPUFeatures f = CPUFeatures::AArch64LegacyBaseline(); |
| std::set<CPUFeatures::Feature> unexpected; |
| std::set<CPUFeatures::Feature> expected; |
| expected.insert(CPUFeatures::kFP); |
| expected.insert(CPUFeatures::kNEON); |
| expected.insert(CPUFeatures::kCRC32); |
| |
| for (CPUFeatures::const_iterator it = f.begin(); it != f.end(); ++it) { |
| if (expected.erase(*it) == 0) unexpected.insert(*it); |
| } |
| CPUFeaturesPredefinedResultCheckHelper(unexpected, expected); |
| } |
| |
| |
| TEST(CPUFeatures_predefined_all) { |
| CPUFeatures f = CPUFeatures::All(); |
| std::set<CPUFeatures::Feature> found; |
| |
| for (CPUFeatures::const_iterator it = f.begin(); it != f.end(); ++it) { |
| found.insert(*it); |
| } |
| VIXL_CHECK(found.size() == CPUFeatures::kNumberOfFeatures); |
| } |
| |
| // The CPUFeaturesScope constructor is templated, and needs an object which |
| // implements `CPUFeatures* GetCPUFeatures()`. This is normally something like |
| // the Assembler, but for the tests we use an architecture-independent wrapper. |
| class GetCPUFeaturesWrapper { |
| public: |
| explicit GetCPUFeaturesWrapper(CPUFeatures* cpu_features) |
| : cpu_features_(cpu_features) {} |
| |
| CPUFeatures* GetCPUFeatures() const { return cpu_features_; } |
| |
| private: |
| CPUFeatures* cpu_features_; |
| }; |
| |
| TEST(CPUFeaturesScope) { |
| // Test that CPUFeaturesScope properly preserves state. |
| |
| CPUFeatures cpu(CPUFeatures::kCRC32, CPUFeatures::kSHA1, CPUFeatures::kAES); |
| GetCPUFeaturesWrapper top_level(&cpu); |
| |
| const CPUFeatures original_outer = cpu; |
| |
| { // Test setting both new and existing features. |
| CPUFeaturesScope outer(&top_level, CPUFeatures::kSHA2, CPUFeatures::kAES); |
| VIXL_CHECK(outer.GetCPUFeatures() == &cpu); |
| VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32, |
| CPUFeatures::kSHA1, |
| CPUFeatures::kSHA2, |
| CPUFeatures::kAES)); |
| |
| // Features can be added or removed directly, in the usual fashion. |
| // (The scope will restore their original status when it ends.) |
| cpu.Combine(CPUFeatures::kSHA1, CPUFeatures::kAtomics); |
| VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32, |
| CPUFeatures::kSHA1, |
| CPUFeatures::kSHA2, |
| CPUFeatures::kAES)); |
| VIXL_CHECK(cpu.Has(CPUFeatures::kAtomics)); |
| |
| cpu.Remove(CPUFeatures::kSHA2, CPUFeatures::kAES); |
| VIXL_CHECK(!cpu.Has(CPUFeatures::kSHA2, CPUFeatures::kAES)); |
| VIXL_CHECK(cpu.Has(CPUFeatures::kCRC32, |
| CPUFeatures::kSHA1, |
| CPUFeatures::kAtomics)); |
| |
| const CPUFeatures original_inner = cpu; |
| |
| // Scopes can be nested. |
| { |
| // A CPUFeaturesScope can be constructed from a CPUFeatures*, or any |
| // (non-local) object that implements `CPUFeatures* GetCPUFeatures()`. |
| // Typically, this would be an Assembler or MacroAssembler, but |
| // CPUFeatureScope itself provides this method, and allows the test to |
| // remain architecture-agnostic. |
| |
| CPUFeatures auth(CPUFeatures::kPAuth, |
| CPUFeatures::kPAuthQARMA, |
| CPUFeatures::kPAuthGeneric, |
| CPUFeatures::kPAuthGenericQARMA); |
| |
| CPUFeaturesScope inner(&outer, auth); |
| VIXL_CHECK(inner.GetCPUFeatures() == &cpu); |
| VIXL_CHECK(cpu.Has(auth.With(CPUFeatures::kCRC32, |
| CPUFeatures::kSHA1, |
| CPUFeatures::kAtomics))); |
| } |
| // Check for equivalence. |
| VIXL_CHECK(cpu.Has(original_inner)); |
| VIXL_CHECK(original_inner.Has(cpu)); |
| } |
| |
| // Check for equivalence. |
| VIXL_CHECK(cpu.Has(original_outer)); |
| VIXL_CHECK(original_outer.Has(cpu)); |
| } |
| |
| TEST(CPUFeatures_infer_from_os) { |
| // Test that CPUFeatures::InferFromOS functions on supported platforms. |
| CPUFeatures os; |
| VIXL_ASSERT(os.HasNoFeatures()); |
| os = CPUFeatures::InferFromOS(); |
| |
| // Every real platform has FP and NEON. However, InferFromOS does not support |
| // every platform, so we also have to tolerate empty results. |
| if (os.HasNoFeatures()) { |
| std::cout << "Warning: CPUFeatures::InferFromOS() returned no results.\n"; |
| } else { |
| std::cout << "CPUFeatures::InferFromOS():\n {" << os << "}\n"; |
| VIXL_CHECK(os.Has(CPUFeatures::kFP)); |
| VIXL_CHECK(os.Has(CPUFeatures::kNEON)); |
| } |
| } |
| |
| TEST(CPUFeatures_infer_from_id_registers) { |
| CPUFeatures os_only = |
| CPUFeatures::InferFromOS(CPUFeatures::kDontQueryIDRegisters); |
| std::cout << "CPUFeatures::InferFromOS(kDontQueryIDRegisters):\n {" |
| << os_only << "}\n"; |
| if (os_only.Has(CPUFeatures::kIDRegisterEmulation)) { |
| CPUFeatures id_regs = CPUFeatures::InferFromIDRegisters(); |
| std::cout << "CPUFeatures::InferFromIDRegisters():\n {" << id_regs |
| << "}\n"; |
| // The ID registers should return at least as many features as the OS |
| // information. This is intended to verify VIXL's InferFromIDRegisters |
| // logic, but it also relies on the OS presenting consistent information. |
| VIXL_CHECK(id_regs.Has(os_only)); |
| |
| // The default InferFromOS should combine its results with |
| // InferFromIDRegisters. |
| CPUFeatures os_auto = CPUFeatures::InferFromOS(); |
| CPUFeatures os_with_id_regs = os_only.With(id_regs); |
| // Check equivalence. |
| VIXL_CHECK(os_auto.Has(os_with_id_regs)); |
| VIXL_CHECK(os_with_id_regs.Has(os_auto)); |
| } else { |
| // Note: This message needs to match REGEXP_MISSING_FEATURES from |
| // tools/threaded_test.py. |
| std::cout << "SKIPPED: Missing features: { " |
| << CPUFeatures::kIDRegisterEmulation << " }\n"; |
| std::cout << "This test requires the following features to run its " |
| "generated code on this CPU: " |
| << CPUFeatures::kIDRegisterEmulation << "\n"; |
| } |
| } |
| |
| } // namespace vixl |