util: Implement atomic operations consistently across compilers and testing for it

Signed-off-by: Yonggang Luo <luoyonggang@gmail.com>
Reviewed-by: Jesse Natalie <jenatali@microsoft.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/18795>
This commit is contained in:
Yonggang Luo 2022-09-06 01:02:29 +08:00 committed by Marge Bot
parent 96e7d1cf0c
commit 4f0f272069
2 changed files with 59 additions and 0 deletions

View File

@ -56,6 +56,9 @@ TYPED_TEST(AtomicAssignment, Test)
r = p_atomic_read(&v);
ASSERT_EQ(r, ones) << "p_atomic_read";
r = p_atomic_read_relaxed(&v);
ASSERT_EQ(r, ones) << "p_atomic_read_relaxed";
v = ones;
r = p_atomic_cmpxchg(&v, 0, 1);
ASSERT_EQ(v, ones) << "p_atomic_cmpxchg";
@ -64,6 +67,11 @@ TYPED_TEST(AtomicAssignment, Test)
r = p_atomic_cmpxchg(&v, ones, 0);
ASSERT_EQ(v, 0) << "p_atomic_cmpxchg";
ASSERT_EQ(r, ones) << "p_atomic_cmpxchg";
v = 0;
r = p_atomic_xchg(&v, ones);
ASSERT_EQ(v, ones) << "p_atomic_xchg";
ASSERT_EQ(r, 0) << "p_atomic_xchg";
}
@ -71,6 +79,7 @@ template <typename T> class AtomicIncrementDecrement : public testing::Test {};
using AtomicIncrementDecrementTypes =
testing::Types<int, unsigned,
int8_t, uint8_t,
int16_t, uint16_t,
int32_t, uint32_t, int64_t, uint64_t>;
@ -114,6 +123,16 @@ TYPED_TEST(AtomicIncrementDecrement, Test)
r = p_atomic_dec_return(&v);
ASSERT_EQ(v, ones) << "p_atomic_dec_return";
ASSERT_EQ(v, r) << "p_atomic_dec_return";
v = 0;
r = p_atomic_add_return(&v, -1);
ASSERT_EQ(v, ones) << "p_atomic_add_return";
ASSERT_EQ(v, r) << "p_atomic_add_return";
v = 0;
r = p_atomic_fetch_add(&v, -1);
ASSERT_EQ(v, ones) << "p_atomic_fetch_add";
ASSERT_EQ(r, 0) << "p_atomic_fetch_add";
}
template <typename T> class AtomicAdd : public testing::Test {};

View File

@ -119,6 +119,16 @@
#include <intrin.h>
#include <assert.h>
__forceinline char _interlockedadd8(char volatile * _Addend, char _Value)
{
return _InterlockedExchangeAdd8(_Addend, _Value) + _Value;
}
__forceinline short _interlockedadd16(short volatile * _Addend, short _Value)
{
return _InterlockedExchangeAdd16(_Addend, _Value) + _Value;
}
/* MSVC supports decltype keyword, but it's only supported on C++ and doesn't
* quite work here; and if a C++-only solution is worthwhile, then it would be
* better to use templates / function overloading, instead of decltype magic.
@ -136,6 +146,7 @@
((void) p_atomic_inc_return(_v))
#define p_atomic_inc_return(_v) (\
sizeof *(_v) == sizeof(char) ? p_atomic_add_return((_v), 1) : \
sizeof *(_v) == sizeof(short) ? _InterlockedIncrement16((short *) (_v)) : \
sizeof *(_v) == sizeof(long) ? _InterlockedIncrement ((long *) (_v)) : \
sizeof *(_v) == sizeof(__int64) ? _interlockedincrement64((__int64 *)(_v)) : \
@ -145,6 +156,7 @@
((void) p_atomic_dec_return(_v))
#define p_atomic_dec_return(_v) (\
sizeof *(_v) == sizeof(char) ? p_atomic_add_return((_v), -1) : \
sizeof *(_v) == sizeof(short) ? _InterlockedDecrement16((short *) (_v)) : \
sizeof *(_v) == sizeof(long) ? _InterlockedDecrement ((long *) (_v)) : \
sizeof *(_v) == sizeof(__int64) ? _interlockeddecrement64((__int64 *)(_v)) : \
@ -154,6 +166,8 @@
((void) p_atomic_fetch_add((_v), (_i)))
#define p_atomic_add_return(_v, _i) (\
sizeof *(_v) == sizeof(char) ? _interlockedadd8 ((char *) (_v), (_i)) : \
sizeof *(_v) == sizeof(short) ? _interlockedadd16((short *) (_v), (_i)) : \
sizeof *(_v) == sizeof(long) ? _interlockedadd ((long *) (_v), (_i)) : \
sizeof *(_v) == sizeof(__int64) ? _interlockedadd64((__int64 *)(_v), (_i)) : \
(assert(!"should not get here"), 0))
@ -180,6 +194,8 @@
#define PIPE_NATIVE_ATOMIC_XCHG
#define p_atomic_xchg(_v, _new) (\
sizeof *(_v) == sizeof(char) ? _InterlockedExchange8 ((char *) (_v), (char) (_new)) : \
sizeof *(_v) == sizeof(short) ? _InterlockedExchange16((short *) (_v), (short) (_new)) : \
sizeof *(_v) == sizeof(long) ? _InterlockedExchange ((long *) (_v), (long) (_new)) : \
sizeof *(_v) == sizeof(__int64) ? _interlockedexchange64((__int64 *)(_v), (__int64)(_new)) : \
(assert(!"should not get here"), 0))
@ -272,6 +288,28 @@
#endif
#ifndef PIPE_NATIVE_ATOMIC_XCHG
static inline uint8_t p_atomic_xchg_8(uint8_t *v, uint8_t i)
{
uint8_t actual = p_atomic_read(v);
uint8_t expected;
do {
expected = actual;
actual = p_atomic_cmpxchg(v, expected, i);
} while (expected != actual);
return actual;
}
static inline uint16_t p_atomic_xchg_16(uint16_t *v, uint16_t i)
{
uint16_t actual = p_atomic_read(v);
uint16_t expected;
do {
expected = actual;
actual = p_atomic_cmpxchg(v, expected, i);
} while (expected != actual);
return actual;
}
static inline uint32_t p_atomic_xchg_32(uint32_t *v, uint32_t i)
{
uint32_t actual = p_atomic_read(v);
@ -295,6 +333,8 @@ static inline uint64_t p_atomic_xchg_64(uint64_t *v, uint64_t i)
}
#define p_atomic_xchg(v, i) (__typeof(*(v)))( \
sizeof(*(v)) == sizeof(uint8_t) ? p_atomic_xchg_8 ((uint8_t *)(v), (uint8_t )(i)) : \
sizeof(*(v)) == sizeof(uint16_t) ? p_atomic_xchg_16((uint16_t *)(v), (uint16_t)(i)) : \
sizeof(*(v)) == sizeof(uint32_t) ? p_atomic_xchg_32((uint32_t *)(v), (uint32_t)(i)) : \
sizeof(*(v)) == sizeof(uint64_t) ? p_atomic_xchg_64((uint64_t *)(v), (uint64_t)(i)) : \
(assert(!"should not get here"), 0))