12ff5a56eSwmbat //===----------------------------------------------------------------------===// 22ff5a56eSwmbat // 32ff5a56eSwmbat // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 42ff5a56eSwmbat // See https://llvm.org/LICENSE.txt for license information. 52ff5a56eSwmbat // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 62ff5a56eSwmbat // 72ff5a56eSwmbat //===----------------------------------------------------------------------===// 82ff5a56eSwmbat 92ff5a56eSwmbat #ifndef ATOMIC_HELPERS_H 102ff5a56eSwmbat #define ATOMIC_HELPERS_H 112ff5a56eSwmbat 122ff5a56eSwmbat #include <cassert> 1370617a1aSNikolas Klauser #include <cstdint> 14cc1dfb37SLouis Dionne #include <cstddef> 15cc1dfb37SLouis Dionne #include <type_traits> 162ff5a56eSwmbat 172ff5a56eSwmbat #include "test_macros.h" 182ff5a56eSwmbat 19cc1dfb37SLouis Dionne #if defined(TEST_COMPILER_CLANG) 20cc1dfb37SLouis Dionne # define TEST_ATOMIC_CHAR_LOCK_FREE __CLANG_ATOMIC_CHAR_LOCK_FREE 21cc1dfb37SLouis Dionne # define TEST_ATOMIC_SHORT_LOCK_FREE __CLANG_ATOMIC_SHORT_LOCK_FREE 22cc1dfb37SLouis Dionne # define TEST_ATOMIC_INT_LOCK_FREE __CLANG_ATOMIC_INT_LOCK_FREE 23cc1dfb37SLouis Dionne # define TEST_ATOMIC_LONG_LOCK_FREE __CLANG_ATOMIC_LONG_LOCK_FREE 24cc1dfb37SLouis Dionne # define TEST_ATOMIC_LLONG_LOCK_FREE __CLANG_ATOMIC_LLONG_LOCK_FREE 25cc1dfb37SLouis Dionne # define TEST_ATOMIC_POINTER_LOCK_FREE __CLANG_ATOMIC_POINTER_LOCK_FREE 26cc1dfb37SLouis Dionne #elif defined(TEST_COMPILER_GCC) 27cc1dfb37SLouis Dionne # define TEST_ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE 28cc1dfb37SLouis Dionne # define TEST_ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE 29cc1dfb37SLouis Dionne # define TEST_ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE 30cc1dfb37SLouis Dionne # define TEST_ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE 31cc1dfb37SLouis Dionne # define TEST_ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE 32cc1dfb37SLouis Dionne # define TEST_ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE 33*886b7612SStephan T. Lavavej #elif defined(TEST_COMPILER_MSVC) 34cc1dfb37SLouis Dionne // This is lifted from STL/stl/inc/atomic on github for the purposes of 35cc1dfb37SLouis Dionne // keeping the tests compiling for MSVC's STL. It's not a perfect solution 36cc1dfb37SLouis Dionne // but at least the tests will keep running. 37cc1dfb37SLouis Dionne // 38cc1dfb37SLouis Dionne // Note MSVC's STL never produces a type that is sometimes lock free, but not always lock free. 39cc1dfb37SLouis Dionne template <class T, size_t Size = sizeof(T)> 40*886b7612SStephan T. Lavavej constexpr int msvc_is_lock_free_macro_value() { 41*886b7612SStephan T. Lavavej return (Size <= 8 && (Size & (Size - 1)) == 0) ? 2 : 0; 42cc1dfb37SLouis Dionne } 43cc1dfb37SLouis Dionne # define TEST_ATOMIC_CHAR_LOCK_FREE ::msvc_is_lock_free_macro_value<char>() 44cc1dfb37SLouis Dionne # define TEST_ATOMIC_SHORT_LOCK_FREE ::msvc_is_lock_free_macro_value<short>() 45cc1dfb37SLouis Dionne # define TEST_ATOMIC_INT_LOCK_FREE ::msvc_is_lock_free_macro_value<int>() 46cc1dfb37SLouis Dionne # define TEST_ATOMIC_LONG_LOCK_FREE ::msvc_is_lock_free_macro_value<long>() 47cc1dfb37SLouis Dionne # define TEST_ATOMIC_LLONG_LOCK_FREE ::msvc_is_lock_free_macro_value<long long>() 48cc1dfb37SLouis Dionne # define TEST_ATOMIC_POINTER_LOCK_FREE ::msvc_is_lock_free_macro_value<void*>() 49cc1dfb37SLouis Dionne #else 50cc1dfb37SLouis Dionne # error "Unknown compiler" 51cc1dfb37SLouis Dionne #endif 52cc1dfb37SLouis Dionne 53cc1dfb37SLouis Dionne #ifdef TEST_COMPILER_CLANG 54cc1dfb37SLouis Dionne # pragma clang diagnostic push 55cc1dfb37SLouis Dionne # pragma clang diagnostic ignored "-Wc++11-extensions" 56cc1dfb37SLouis Dionne #endif 57cc1dfb37SLouis Dionne 58cc1dfb37SLouis Dionne enum class LockFreeStatus : int { unknown = -1, never = 0, sometimes = 1, always = 2 }; 59cc1dfb37SLouis Dionne 60cc1dfb37SLouis Dionne // We should really be checking whether the alignment of T is greater-than-or-equal-to the alignment required 61cc1dfb37SLouis Dionne // for T to be atomic, but this is basically impossible to implement portably. Instead, we assume that any type 62cc1dfb37SLouis Dionne // aligned to at least its size is going to be atomic if there exists atomic operations for that size at all, 63cc1dfb37SLouis Dionne // which is true on most platforms. This technically reduces our test coverage in the sense that if a type has 64cc1dfb37SLouis Dionne // an alignment requirement less than its size but could still be made lockfree, LockFreeStatusInfo will report 65cc1dfb37SLouis Dionne // that we don't know whether it is lockfree or not. 66cc1dfb37SLouis Dionne #define COMPARE_TYPES(T, FundamentalT) (sizeof(T) == sizeof(FundamentalT) && TEST_ALIGNOF(T) >= sizeof(T)) 67cc1dfb37SLouis Dionne 68cc1dfb37SLouis Dionne template <class T> 69cc1dfb37SLouis Dionne struct LockFreeStatusInfo { 70cc1dfb37SLouis Dionne static const LockFreeStatus value = LockFreeStatus( 71cc1dfb37SLouis Dionne COMPARE_TYPES(T, char) 72cc1dfb37SLouis Dionne ? TEST_ATOMIC_CHAR_LOCK_FREE 73cc1dfb37SLouis Dionne : (COMPARE_TYPES(T, short) 74cc1dfb37SLouis Dionne ? TEST_ATOMIC_SHORT_LOCK_FREE 75cc1dfb37SLouis Dionne : (COMPARE_TYPES(T, int) 76cc1dfb37SLouis Dionne ? TEST_ATOMIC_INT_LOCK_FREE 77cc1dfb37SLouis Dionne : (COMPARE_TYPES(T, long) 78cc1dfb37SLouis Dionne ? TEST_ATOMIC_LONG_LOCK_FREE 79cc1dfb37SLouis Dionne : (COMPARE_TYPES(T, long long) 80cc1dfb37SLouis Dionne ? TEST_ATOMIC_LLONG_LOCK_FREE 81cc1dfb37SLouis Dionne : (COMPARE_TYPES(T, void*) ? TEST_ATOMIC_POINTER_LOCK_FREE : -1)))))); 82cc1dfb37SLouis Dionne 83cc1dfb37SLouis Dionne static const bool status_known = LockFreeStatusInfo::value != LockFreeStatus::unknown; 84cc1dfb37SLouis Dionne }; 85cc1dfb37SLouis Dionne 86cc1dfb37SLouis Dionne #undef COMPARE_TYPES 87cc1dfb37SLouis Dionne 88cc1dfb37SLouis Dionne // This doesn't work in C++03 due to issues with scoped enumerations. Just disable the test. 89cc1dfb37SLouis Dionne #if TEST_STD_VER >= 11 90cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<char>::status_known, ""); 91cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<short>::status_known, ""); 92cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<int>::status_known, ""); 93cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<long>::status_known, ""); 94cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<void*>::status_known, ""); 95cc1dfb37SLouis Dionne 96cc1dfb37SLouis Dionne // long long is a bit funky: on some platforms, its alignment is 4 bytes but its size is 97cc1dfb37SLouis Dionne // 8 bytes. In that case, atomics may or may not be lockfree based on their address. 98cc1dfb37SLouis Dionne static_assert(alignof(long long) == sizeof(long long) ? LockFreeStatusInfo<long long>::status_known : true, ""); 99cc1dfb37SLouis Dionne 100cc1dfb37SLouis Dionne // Those should always be lock free: hardcode some expected values to make sure our tests are actually 101cc1dfb37SLouis Dionne // testing something meaningful. 102cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<char>::value == LockFreeStatus::always, ""); 103cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<short>::value == LockFreeStatus::always, ""); 104cc1dfb37SLouis Dionne static_assert(LockFreeStatusInfo<int>::value == LockFreeStatus::always, ""); 105cc1dfb37SLouis Dionne #endif 106cc1dfb37SLouis Dionne 107cc1dfb37SLouis Dionne // These macros are somewhat suprising to use, since they take the values 0, 1, or 2. 108cc1dfb37SLouis Dionne // To make the tests clearer, get rid of them in preference of LockFreeStatusInfo. 109cc1dfb37SLouis Dionne #undef TEST_ATOMIC_CHAR_LOCK_FREE 110cc1dfb37SLouis Dionne #undef TEST_ATOMIC_SHORT_LOCK_FREE 111cc1dfb37SLouis Dionne #undef TEST_ATOMIC_INT_LOCK_FREE 112cc1dfb37SLouis Dionne #undef TEST_ATOMIC_LONG_LOCK_FREE 113cc1dfb37SLouis Dionne #undef TEST_ATOMIC_LLONG_LOCK_FREE 114cc1dfb37SLouis Dionne #undef TEST_ATOMIC_POINTER_LOCK_FREE 115cc1dfb37SLouis Dionne 116cc1dfb37SLouis Dionne #ifdef TEST_COMPILER_CLANG 117cc1dfb37SLouis Dionne # pragma clang diagnostic pop 118cc1dfb37SLouis Dionne #endif 119cc1dfb37SLouis Dionne 1202ff5a56eSwmbat struct UserAtomicType { 1212ff5a56eSwmbat int i; 1222ff5a56eSwmbat 1232ff5a56eSwmbat explicit UserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {} 1242ff5a56eSwmbat 1252ff5a56eSwmbat friend bool operator==(const UserAtomicType& x, const UserAtomicType& y) { return x.i == y.i; } 1262ff5a56eSwmbat }; 1272ff5a56eSwmbat 1282ff5a56eSwmbat /* 1292ff5a56eSwmbat 1302ff5a56eSwmbat Enable these once we have P0528 1312ff5a56eSwmbat 1322ff5a56eSwmbat struct WeirdUserAtomicType 1332ff5a56eSwmbat { 1342ff5a56eSwmbat char i, j, k; // the 3 chars of doom 1352ff5a56eSwmbat 1362ff5a56eSwmbat explicit WeirdUserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {} 1372ff5a56eSwmbat 1382ff5a56eSwmbat friend bool operator==(const WeirdUserAtomicType& x, const WeirdUserAtomicType& y) 1392ff5a56eSwmbat { return x.i == y.i; } 1402ff5a56eSwmbat }; 1412ff5a56eSwmbat 1422ff5a56eSwmbat struct PaddedUserAtomicType 1432ff5a56eSwmbat { 1442ff5a56eSwmbat char i; int j; // probably lock-free? 1452ff5a56eSwmbat 1462ff5a56eSwmbat explicit PaddedUserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {} 1472ff5a56eSwmbat 1482ff5a56eSwmbat friend bool operator==(const PaddedUserAtomicType& x, const PaddedUserAtomicType& y) 1492ff5a56eSwmbat { return x.i == y.i; } 1502ff5a56eSwmbat }; 1512ff5a56eSwmbat 1522ff5a56eSwmbat */ 1532ff5a56eSwmbat 1542ff5a56eSwmbat struct LargeUserAtomicType { 1552ff5a56eSwmbat int a[128]; /* decidedly not lock-free */ 1562ff5a56eSwmbat 1572ff5a56eSwmbat LargeUserAtomicType(int d = 0) TEST_NOEXCEPT { 1582ff5a56eSwmbat for (auto&& e : a) 1592ff5a56eSwmbat e = d++; 1602ff5a56eSwmbat } 1612ff5a56eSwmbat 1622ff5a56eSwmbat friend bool operator==(LargeUserAtomicType const& x, LargeUserAtomicType const& y) TEST_NOEXCEPT { 1632ff5a56eSwmbat for (int i = 0; i < 128; ++i) 1642ff5a56eSwmbat if (x.a[i] != y.a[i]) 1652ff5a56eSwmbat return false; 1662ff5a56eSwmbat return true; 1672ff5a56eSwmbat } 1682ff5a56eSwmbat }; 1692ff5a56eSwmbat 1702ff5a56eSwmbat template <template <class TestArg> class TestFunctor> 1712ff5a56eSwmbat struct TestEachIntegralType { 1722ff5a56eSwmbat void operator()() const { 1732ff5a56eSwmbat TestFunctor<char>()(); 1742ff5a56eSwmbat TestFunctor<signed char>()(); 1752ff5a56eSwmbat TestFunctor<unsigned char>()(); 1762ff5a56eSwmbat TestFunctor<short>()(); 1772ff5a56eSwmbat TestFunctor<unsigned short>()(); 1782ff5a56eSwmbat TestFunctor<int>()(); 1792ff5a56eSwmbat TestFunctor<unsigned int>()(); 1802ff5a56eSwmbat TestFunctor<long>()(); 1812ff5a56eSwmbat TestFunctor<unsigned long>()(); 1822ff5a56eSwmbat TestFunctor<long long>()(); 1832ff5a56eSwmbat TestFunctor<unsigned long long>()(); 1847efe3887SArthur O'Dwyer TestFunctor<wchar_t>()(); 1852ff5a56eSwmbat #if TEST_STD_VER > 17 && defined(__cpp_char8_t) 1862ff5a56eSwmbat TestFunctor<char8_t>()(); 1872ff5a56eSwmbat #endif 1882ff5a56eSwmbat TestFunctor<char16_t>()(); 1892ff5a56eSwmbat TestFunctor<char32_t>()(); 190bd5d0feeSMark de Wever TestFunctor<std::int8_t>()(); 191bd5d0feeSMark de Wever TestFunctor<std::uint8_t>()(); 192bd5d0feeSMark de Wever TestFunctor<std::int16_t>()(); 193bd5d0feeSMark de Wever TestFunctor<std::uint16_t>()(); 194bd5d0feeSMark de Wever TestFunctor<std::int32_t>()(); 195da79d6e1SMark de Wever TestFunctor<std::uint32_t>()(); 196bd5d0feeSMark de Wever TestFunctor<std::int64_t>()(); 197bd5d0feeSMark de Wever TestFunctor<std::uint64_t>()(); 1982ff5a56eSwmbat } 1992ff5a56eSwmbat }; 2002ff5a56eSwmbat 2012ff5a56eSwmbat template <template <class TestArg> class TestFunctor> 2022ff5a56eSwmbat struct TestEachFloatingPointType { 2032ff5a56eSwmbat void operator()() const { 2042ff5a56eSwmbat TestFunctor<float>()(); 2052ff5a56eSwmbat TestFunctor<double>()(); 2062ff5a56eSwmbat TestFunctor<long double>()(); 2072ff5a56eSwmbat } 2082ff5a56eSwmbat }; 2092ff5a56eSwmbat 2102ff5a56eSwmbat template <template <class TestArg> class TestFunctor> 2112ff5a56eSwmbat struct TestEachPointerType { 2122ff5a56eSwmbat void operator()() const { 2132ff5a56eSwmbat TestFunctor<int*>()(); 2142ff5a56eSwmbat TestFunctor<const int*>()(); 2152ff5a56eSwmbat } 2162ff5a56eSwmbat }; 2172ff5a56eSwmbat 2182ff5a56eSwmbat template <template <class TestArg> class TestFunctor> 2192ff5a56eSwmbat struct TestEachAtomicType { 2202ff5a56eSwmbat void operator()() const { 2212ff5a56eSwmbat TestEachIntegralType<TestFunctor>()(); 2229232591bSLouis Dionne TestEachPointerType<TestFunctor>()(); 2232ff5a56eSwmbat TestFunctor<UserAtomicType>()(); 2242ff5a56eSwmbat /* 2252ff5a56eSwmbat Note: These aren't going to be lock-free, 22685a847fdSKonstantin Varlamov so some libatomic.a is necessary. 2272ff5a56eSwmbat */ 2282ff5a56eSwmbat TestFunctor<LargeUserAtomicType>()(); 2292ff5a56eSwmbat /* 2302ff5a56eSwmbat Enable these once we have P0528 2312ff5a56eSwmbat 2322ff5a56eSwmbat TestFunctor<PaddedUserAtomicType>()(); 2332ff5a56eSwmbat TestFunctor<WeirdUserAtomicType>()(); 2342ff5a56eSwmbat */ 2352ff5a56eSwmbat TestFunctor<float>()(); 2362ff5a56eSwmbat TestFunctor<double>()(); 2372ff5a56eSwmbat } 2382ff5a56eSwmbat }; 2392ff5a56eSwmbat 2402ff5a56eSwmbat #endif // ATOMIC_HELPERS_H 241