Skip to content

Commit

Permalink
Implement atomic_t<>::release
Browse files Browse the repository at this point in the history
More relaxed store with release memory order
  • Loading branch information
Nekotekina committed Jan 28, 2019
1 parent bd49398 commit cd58087
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 26 deletions.
55 changes: 49 additions & 6 deletions Utilities/Atomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <functional>

// Helper class, provides access to compiler-specific atomic intrinsics
template<typename T, std::size_t Size = sizeof(T)>
template <typename T, std::size_t Size = sizeof(T)>
struct atomic_storage
{
static_assert(sizeof(T) <= 16 && sizeof(T) == alignof(T), "atomic_storage<> error: invalid type");
Expand All @@ -29,6 +29,11 @@ struct atomic_storage
__atomic_store(&dest, &value, __ATOMIC_SEQ_CST);
}

static inline void release(T& dest, T value)
{
__atomic_store(&dest, &value, __ATOMIC_RELEASE);
}

static inline T exchange(T& dest, T value)
{
T result;
Expand Down Expand Up @@ -176,7 +181,7 @@ struct atomic_storage

/* The rest: ugly MSVC intrinsics + inline asm implementations */

template<typename T>
template <typename T>
struct atomic_storage<T, 1> : atomic_storage<T, 0>
{
#ifdef _MSC_VER
Expand All @@ -200,6 +205,12 @@ struct atomic_storage<T, 1> : atomic_storage<T, 0>
_InterlockedExchange8((volatile char*)&dest, (char&)value);
}

static inline void release(T& dest, T value)
{
_ReadWriteBarrier();
*(volatile char*)&dest = (char&)value;
}

static inline T exchange(T& dest, T value)
{
char r = _InterlockedExchange8((volatile char*)&dest, (char&)value);
Expand Down Expand Up @@ -232,7 +243,7 @@ struct atomic_storage<T, 1> : atomic_storage<T, 0>
#endif
};

template<typename T>
template <typename T>
struct atomic_storage<T, 2> : atomic_storage<T, 0>
{
#ifdef _MSC_VER
Expand All @@ -256,6 +267,12 @@ struct atomic_storage<T, 2> : atomic_storage<T, 0>
_InterlockedExchange16((volatile short*)&dest, (short&)value);
}

static inline void release(T& dest, T value)
{
_ReadWriteBarrier();
*(volatile short*)&dest = (short&)value;
}

static inline T exchange(T& dest, T value)
{
short r = _InterlockedExchange16((volatile short*)&dest, (short&)value);
Expand Down Expand Up @@ -324,7 +341,7 @@ struct atomic_storage<T, 2> : atomic_storage<T, 0>
#endif
};

template<typename T>
template <typename T>
struct atomic_storage<T, 4> : atomic_storage<T, 0>
{
#ifdef _MSC_VER
Expand All @@ -348,6 +365,12 @@ struct atomic_storage<T, 4> : atomic_storage<T, 0>
_InterlockedExchange((volatile long*)&dest, (long&)value);
}

static inline void release(T& dest, T value)
{
_ReadWriteBarrier();
*(volatile long*)&dest = (long&)value;
}

static inline T exchange(T& dest, T value)
{
long r = _InterlockedExchange((volatile long*)&dest, (long&)value);
Expand Down Expand Up @@ -423,7 +446,7 @@ struct atomic_storage<T, 4> : atomic_storage<T, 0>
#endif
};

template<typename T>
template <typename T>
struct atomic_storage<T, 8> : atomic_storage<T, 0>
{
#ifdef _MSC_VER
Expand All @@ -447,6 +470,12 @@ struct atomic_storage<T, 8> : atomic_storage<T, 0>
_InterlockedExchange64((volatile llong*)&dest, (llong&)value);
}

static inline void release(T& dest, T value)
{
_ReadWriteBarrier();
*(volatile llong*)&dest = (llong&)value;
}

static inline T exchange(T& dest, T value)
{
llong r = _InterlockedExchange64((volatile llong*)&dest, (llong&)value);
Expand Down Expand Up @@ -525,7 +554,7 @@ struct atomic_storage<T, 8> : atomic_storage<T, 0>
#endif
};

template<typename T>
template <typename T>
struct atomic_storage<T, 16> : atomic_storage<T, 0>
{
#ifdef _MSC_VER
Expand All @@ -550,6 +579,14 @@ struct atomic_storage<T, 16> : atomic_storage<T, 0>
while (!_InterlockedCompareExchange128((volatile llong*)&dest, hi, lo, cmp));
}

static inline void release(T& dest, T value)
{
llong lo = *(llong*)&value;
llong hi = *((llong*)&value + 1);
llong cmp[2]{ *(volatile llong*)&dest, *((volatile llong*)&dest + 1) };
while (!_InterlockedCompareExchange128((volatile llong*)&dest, hi, lo, cmp));
}

static inline T exchange(T& dest, T value)
{
llong lo = *(llong*)&value;
Expand Down Expand Up @@ -764,6 +801,12 @@ class atomic_t
return rhs;
}

// Atomically write data with release memory order (faster on x86)
void release(const type& rhs)
{
atomic_storage<type>::release(m_data, rhs);
}

// Atomically replace data with value, return previous data value
type exchange(const type& rhs)
{
Expand Down
18 changes: 7 additions & 11 deletions rpcs3/Emu/Cell/SPURecompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -543,11 +543,7 @@ void spu_recompiler_base::dispatch(spu_thread& spu, void*, u8* rip)
// If code verification failed from a patched patchpoint, clear it with a single NOP
if (rip)
{
#ifdef _MSC_VER
*(volatile u64*)(rip) = 0x841f0f;
#else
__atomic_store_n(reinterpret_cast<u64*>(rip), 0x841f0f, __ATOMIC_RELAXED);
#endif
atomic_storage<u64>::release(*reinterpret_cast<u64*>(rip), 0x841f0f);
}

// Second attempt (recover from the recursion after repeated unsuccessful trampoline call)
Expand Down Expand Up @@ -580,7 +576,11 @@ void spu_recompiler_base::branch(spu_thread& spu, void*, u8* rip)
// Overwrite jump to this function with jump to the compiled function
const s64 rel = reinterpret_cast<u64>(func) - reinterpret_cast<u64>(rip) - 5;

alignas(8) u8 bytes[8];
union
{
u8 bytes[8];
u64 result;
};

if (rel >= INT32_MIN && rel <= INT32_MAX)
{
Expand Down Expand Up @@ -609,11 +609,7 @@ void spu_recompiler_base::branch(spu_thread& spu, void*, u8* rip)
std::memset(bytes + 3, 0x00, 5);
}

#ifdef _MSC_VER
*(volatile u64*)(rip) = *reinterpret_cast<u64*>(+bytes);
#else
__atomic_store_n(reinterpret_cast<u64*>(rip), *reinterpret_cast<u64*>(+bytes), __ATOMIC_RELAXED);
#endif
atomic_storage<u64>::release(*reinterpret_cast<u64*>(rip), result);
}

std::vector<u32> spu_recompiler_base::block(const be_t<u32>* ls, u32 entry_point)
Expand Down
7 changes: 1 addition & 6 deletions rpcs3/Emu/Cell/SPUThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,7 @@ struct spu_channel

void set_value(u32 value, bool count = true)
{
const u64 new_data = u64{count} << off_count | value;
#ifdef _MSC_VER
const_cast<volatile u64&>(data.raw()) = new_data;
#else
__atomic_store_n(&data.raw(), new_data, __ATOMIC_RELAXED);
#endif
data.release(u64{count} << off_count | value);
}

u32 get_value()
Expand Down
6 changes: 3 additions & 3 deletions rpcs3/Emu/Memory/vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ namespace vm
if (!lock && lock.compare_and_swap_test(0, lock_info))
{
return &lock;
}
}
}
}
}
Expand Down Expand Up @@ -256,7 +256,7 @@ namespace vm
{
const u64 value = lock;

// Test beginning address
// Test beginning address
if (static_cast<u32>(value) > addr)
{
break;
Expand Down Expand Up @@ -295,7 +295,7 @@ namespace vm

writer_lock::~writer_lock()
{
g_addr_lock.raw() = 0;
g_addr_lock.release(0);
g_mutex.unlock();
}

Expand Down

0 comments on commit cd58087

Please sign in to comment.