mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 13:16:06 +00:00

Followup to #99570. * `TEST_COMPILER_MSVC` must be tested for `defined`ness, as it is everywhere else. + Definition:52a7116f5c/libcxx/test/support/test_macros.h (L71-L72)
+ Example usage:52a7116f5c/libcxx/test/std/utilities/function.objects/func.not_fn/not_fn.pass.cpp (L248)
+ Fixes: `llvm-project\libcxx\test\support\atomic_helpers.h(33): fatal error C1017: invalid integer constant expression` * Fix bogus return type: `msvc_is_lock_free_macro_value()` returns `2` or `0`, so it needs to return `int`. + Fixes: `llvm-project\libcxx\test\support\atomic_helpers.h(41): warning C4305: 'return': truncation from 'int' to 'bool'` * Clarity improvement: also add parens when mixing bitwise with arithmetic operators.
241 lines
8.5 KiB
C++
241 lines
8.5 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef ATOMIC_HELPERS_H
|
|
#define ATOMIC_HELPERS_H
|
|
|
|
#include <cassert>
|
|
#include <cstdint>
|
|
#include <cstddef>
|
|
#include <type_traits>
|
|
|
|
#include "test_macros.h"
|
|
|
|
#if defined(TEST_COMPILER_CLANG)
|
|
# define TEST_ATOMIC_CHAR_LOCK_FREE __CLANG_ATOMIC_CHAR_LOCK_FREE
|
|
# define TEST_ATOMIC_SHORT_LOCK_FREE __CLANG_ATOMIC_SHORT_LOCK_FREE
|
|
# define TEST_ATOMIC_INT_LOCK_FREE __CLANG_ATOMIC_INT_LOCK_FREE
|
|
# define TEST_ATOMIC_LONG_LOCK_FREE __CLANG_ATOMIC_LONG_LOCK_FREE
|
|
# define TEST_ATOMIC_LLONG_LOCK_FREE __CLANG_ATOMIC_LLONG_LOCK_FREE
|
|
# define TEST_ATOMIC_POINTER_LOCK_FREE __CLANG_ATOMIC_POINTER_LOCK_FREE
|
|
#elif defined(TEST_COMPILER_GCC)
|
|
# define TEST_ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE
|
|
# define TEST_ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE
|
|
# define TEST_ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE
|
|
# define TEST_ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE
|
|
# define TEST_ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE
|
|
# define TEST_ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE
|
|
#elif defined(TEST_COMPILER_MSVC)
|
|
// This is lifted from STL/stl/inc/atomic on github for the purposes of
|
|
// keeping the tests compiling for MSVC's STL. It's not a perfect solution
|
|
// but at least the tests will keep running.
|
|
//
|
|
// Note MSVC's STL never produces a type that is sometimes lock free, but not always lock free.
|
|
template <class T, size_t Size = sizeof(T)>
|
|
constexpr int msvc_is_lock_free_macro_value() {
|
|
return (Size <= 8 && (Size & (Size - 1)) == 0) ? 2 : 0;
|
|
}
|
|
# define TEST_ATOMIC_CHAR_LOCK_FREE ::msvc_is_lock_free_macro_value<char>()
|
|
# define TEST_ATOMIC_SHORT_LOCK_FREE ::msvc_is_lock_free_macro_value<short>()
|
|
# define TEST_ATOMIC_INT_LOCK_FREE ::msvc_is_lock_free_macro_value<int>()
|
|
# define TEST_ATOMIC_LONG_LOCK_FREE ::msvc_is_lock_free_macro_value<long>()
|
|
# define TEST_ATOMIC_LLONG_LOCK_FREE ::msvc_is_lock_free_macro_value<long long>()
|
|
# define TEST_ATOMIC_POINTER_LOCK_FREE ::msvc_is_lock_free_macro_value<void*>()
|
|
#else
|
|
# error "Unknown compiler"
|
|
#endif
|
|
|
|
#ifdef TEST_COMPILER_CLANG
|
|
# pragma clang diagnostic push
|
|
# pragma clang diagnostic ignored "-Wc++11-extensions"
|
|
#endif
|
|
|
|
enum class LockFreeStatus : int { unknown = -1, never = 0, sometimes = 1, always = 2 };
|
|
|
|
// We should really be checking whether the alignment of T is greater-than-or-equal-to the alignment required
|
|
// for T to be atomic, but this is basically impossible to implement portably. Instead, we assume that any type
|
|
// aligned to at least its size is going to be atomic if there exists atomic operations for that size at all,
|
|
// which is true on most platforms. This technically reduces our test coverage in the sense that if a type has
|
|
// an alignment requirement less than its size but could still be made lockfree, LockFreeStatusInfo will report
|
|
// that we don't know whether it is lockfree or not.
|
|
#define COMPARE_TYPES(T, FundamentalT) (sizeof(T) == sizeof(FundamentalT) && TEST_ALIGNOF(T) >= sizeof(T))
|
|
|
|
template <class T>
|
|
struct LockFreeStatusInfo {
|
|
static const LockFreeStatus value = LockFreeStatus(
|
|
COMPARE_TYPES(T, char)
|
|
? TEST_ATOMIC_CHAR_LOCK_FREE
|
|
: (COMPARE_TYPES(T, short)
|
|
? TEST_ATOMIC_SHORT_LOCK_FREE
|
|
: (COMPARE_TYPES(T, int)
|
|
? TEST_ATOMIC_INT_LOCK_FREE
|
|
: (COMPARE_TYPES(T, long)
|
|
? TEST_ATOMIC_LONG_LOCK_FREE
|
|
: (COMPARE_TYPES(T, long long)
|
|
? TEST_ATOMIC_LLONG_LOCK_FREE
|
|
: (COMPARE_TYPES(T, void*) ? TEST_ATOMIC_POINTER_LOCK_FREE : -1))))));
|
|
|
|
static const bool status_known = LockFreeStatusInfo::value != LockFreeStatus::unknown;
|
|
};
|
|
|
|
#undef COMPARE_TYPES
|
|
|
|
// This doesn't work in C++03 due to issues with scoped enumerations. Just disable the test.
|
|
#if TEST_STD_VER >= 11
|
|
static_assert(LockFreeStatusInfo<char>::status_known, "");
|
|
static_assert(LockFreeStatusInfo<short>::status_known, "");
|
|
static_assert(LockFreeStatusInfo<int>::status_known, "");
|
|
static_assert(LockFreeStatusInfo<long>::status_known, "");
|
|
static_assert(LockFreeStatusInfo<void*>::status_known, "");
|
|
|
|
// long long is a bit funky: on some platforms, its alignment is 4 bytes but its size is
|
|
// 8 bytes. In that case, atomics may or may not be lockfree based on their address.
|
|
static_assert(alignof(long long) == sizeof(long long) ? LockFreeStatusInfo<long long>::status_known : true, "");
|
|
|
|
// Those should always be lock free: hardcode some expected values to make sure our tests are actually
|
|
// testing something meaningful.
|
|
static_assert(LockFreeStatusInfo<char>::value == LockFreeStatus::always, "");
|
|
static_assert(LockFreeStatusInfo<short>::value == LockFreeStatus::always, "");
|
|
static_assert(LockFreeStatusInfo<int>::value == LockFreeStatus::always, "");
|
|
#endif
|
|
|
|
// These macros are somewhat suprising to use, since they take the values 0, 1, or 2.
|
|
// To make the tests clearer, get rid of them in preference of LockFreeStatusInfo.
|
|
#undef TEST_ATOMIC_CHAR_LOCK_FREE
|
|
#undef TEST_ATOMIC_SHORT_LOCK_FREE
|
|
#undef TEST_ATOMIC_INT_LOCK_FREE
|
|
#undef TEST_ATOMIC_LONG_LOCK_FREE
|
|
#undef TEST_ATOMIC_LLONG_LOCK_FREE
|
|
#undef TEST_ATOMIC_POINTER_LOCK_FREE
|
|
|
|
#ifdef TEST_COMPILER_CLANG
|
|
# pragma clang diagnostic pop
|
|
#endif
|
|
|
|
struct UserAtomicType {
|
|
int i;
|
|
|
|
explicit UserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {}
|
|
|
|
friend bool operator==(const UserAtomicType& x, const UserAtomicType& y) { return x.i == y.i; }
|
|
};
|
|
|
|
/*
|
|
|
|
Enable these once we have P0528
|
|
|
|
struct WeirdUserAtomicType
|
|
{
|
|
char i, j, k; // the 3 chars of doom
|
|
|
|
explicit WeirdUserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {}
|
|
|
|
friend bool operator==(const WeirdUserAtomicType& x, const WeirdUserAtomicType& y)
|
|
{ return x.i == y.i; }
|
|
};
|
|
|
|
struct PaddedUserAtomicType
|
|
{
|
|
char i; int j; // probably lock-free?
|
|
|
|
explicit PaddedUserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {}
|
|
|
|
friend bool operator==(const PaddedUserAtomicType& x, const PaddedUserAtomicType& y)
|
|
{ return x.i == y.i; }
|
|
};
|
|
|
|
*/
|
|
|
|
struct LargeUserAtomicType {
|
|
int a[128]; /* decidedly not lock-free */
|
|
|
|
LargeUserAtomicType(int d = 0) TEST_NOEXCEPT {
|
|
for (auto&& e : a)
|
|
e = d++;
|
|
}
|
|
|
|
friend bool operator==(LargeUserAtomicType const& x, LargeUserAtomicType const& y) TEST_NOEXCEPT {
|
|
for (int i = 0; i < 128; ++i)
|
|
if (x.a[i] != y.a[i])
|
|
return false;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template <template <class TestArg> class TestFunctor>
|
|
struct TestEachIntegralType {
|
|
void operator()() const {
|
|
TestFunctor<char>()();
|
|
TestFunctor<signed char>()();
|
|
TestFunctor<unsigned char>()();
|
|
TestFunctor<short>()();
|
|
TestFunctor<unsigned short>()();
|
|
TestFunctor<int>()();
|
|
TestFunctor<unsigned int>()();
|
|
TestFunctor<long>()();
|
|
TestFunctor<unsigned long>()();
|
|
TestFunctor<long long>()();
|
|
TestFunctor<unsigned long long>()();
|
|
TestFunctor<wchar_t>()();
|
|
#if TEST_STD_VER > 17 && defined(__cpp_char8_t)
|
|
TestFunctor<char8_t>()();
|
|
#endif
|
|
TestFunctor<char16_t>()();
|
|
TestFunctor<char32_t>()();
|
|
TestFunctor<std::int8_t>()();
|
|
TestFunctor<std::uint8_t>()();
|
|
TestFunctor<std::int16_t>()();
|
|
TestFunctor<std::uint16_t>()();
|
|
TestFunctor<std::int32_t>()();
|
|
TestFunctor<std::uint32_t>()();
|
|
TestFunctor<std::int64_t>()();
|
|
TestFunctor<std::uint64_t>()();
|
|
}
|
|
};
|
|
|
|
template <template <class TestArg> class TestFunctor>
|
|
struct TestEachFloatingPointType {
|
|
void operator()() const {
|
|
TestFunctor<float>()();
|
|
TestFunctor<double>()();
|
|
TestFunctor<long double>()();
|
|
}
|
|
};
|
|
|
|
template <template <class TestArg> class TestFunctor>
|
|
struct TestEachPointerType {
|
|
void operator()() const {
|
|
TestFunctor<int*>()();
|
|
TestFunctor<const int*>()();
|
|
}
|
|
};
|
|
|
|
template <template <class TestArg> class TestFunctor>
|
|
struct TestEachAtomicType {
|
|
void operator()() const {
|
|
TestEachIntegralType<TestFunctor>()();
|
|
TestEachPointerType<TestFunctor>()();
|
|
TestFunctor<UserAtomicType>()();
|
|
/*
|
|
Note: These aren't going to be lock-free,
|
|
so some libatomic.a is necessary.
|
|
*/
|
|
TestFunctor<LargeUserAtomicType>()();
|
|
/*
|
|
Enable these once we have P0528
|
|
|
|
TestFunctor<PaddedUserAtomicType>()();
|
|
TestFunctor<WeirdUserAtomicType>()();
|
|
*/
|
|
TestFunctor<float>()();
|
|
TestFunctor<double>()();
|
|
}
|
|
};
|
|
|
|
#endif // ATOMIC_HELPERS_H
|