Files
AnyPS5/core/libs/tests/GuestAtomicBuiltins.cpp
hypermistandClaude Opus 5.5 6d2708ab95 feat(libc): implement the 16-byte __sync builtins and __atomic_is_lock_free
__sync_fetch_and_{add,sub,and,or,xor}_16 and __sync_lock_test_and_set_16 are
cmpxchg16b loops returning the previous value, and __atomic_is_lock_free
answers by size and alignment, as in the libc.prx bundled with PPSA08427. Its
generic __atomic_load/store/exchange/compare_exchange are a ud2, so they throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8tinbP1MUpUaDsbNWnGnu
2026-10-16 00:59:12 +13:00

87 lines
3.9 KiB
C++

#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <stdexcept>
#include <thread>
using Uint128 = unsigned __int128;
extern "C" {
void APS5_VABI __atomic_load_nid_postfix(std::size_t, void*, void*, int);
void APS5_VABI __atomic_store_nid_postfix(std::size_t, void*, void*, int);
void APS5_VABI __atomic_exchange_nid_postfix(std::size_t, void*, void*, void*, int);
bool APS5_VABI __atomic_compare_exchange_nid_postfix(std::size_t, void*, void*, void*, int, int);
bool APS5_VABI __atomic_is_lock_free_nid_postfix(std::size_t, const void*);
Uint128 APS5_VABI __sync_fetch_and_add_16_nid_postfix(Uint128*, Uint128);
Uint128 APS5_VABI __sync_fetch_and_sub_16_nid_postfix(Uint128*, Uint128);
Uint128 APS5_VABI __sync_fetch_and_and_16_nid_postfix(Uint128*, Uint128);
Uint128 APS5_VABI __sync_fetch_and_or_16_nid_postfix(Uint128*, Uint128);
Uint128 APS5_VABI __sync_fetch_and_xor_16_nid_postfix(Uint128*, Uint128);
Uint128 APS5_VABI __sync_lock_test_and_set_16_nid_postfix(Uint128*, Uint128);
}
static void Require(bool value) { if (!value) std::abort(); }
template<typename TFunction>
static void RequireThrows(TFunction function) {
try {
function();
} catch (const std::runtime_error&) {
return;
}
std::abort();
}
static Uint128 Make(std::uint64_t high, std::uint64_t low) { return (static_cast<Uint128>(high) << 64) | low; }
static void CheckLockFree() {
alignas(16) unsigned char buffer[32] = {};
Require(__atomic_is_lock_free_nid_postfix(0, buffer + 1) && __atomic_is_lock_free_nid_postfix(1, buffer + 3));
Require(__atomic_is_lock_free_nid_postfix(2, buffer + 2) && !__atomic_is_lock_free_nid_postfix(2, buffer + 1));
Require(__atomic_is_lock_free_nid_postfix(4, buffer + 4) && !__atomic_is_lock_free_nid_postfix(4, buffer + 2));
Require(__atomic_is_lock_free_nid_postfix(8, buffer + 8) && !__atomic_is_lock_free_nid_postfix(8, buffer + 4));
Require(__atomic_is_lock_free_nid_postfix(16, buffer + 16) && !__atomic_is_lock_free_nid_postfix(16, buffer + 8));
Require(!__atomic_is_lock_free_nid_postfix(3, buffer) && !__atomic_is_lock_free_nid_postfix(32, buffer));
}
static void CheckGenericTraps() {
alignas(16) unsigned char object[24] = {};
unsigned char other[24] = {};
RequireThrows([&] { __atomic_load_nid_postfix(sizeof(object), object, other, 5); });
RequireThrows([&] { __atomic_store_nid_postfix(sizeof(object), object, other, 5); });
RequireThrows([&] { __atomic_exchange_nid_postfix(sizeof(object), object, other, other, 5); });
RequireThrows([&] { __atomic_compare_exchange_nid_postfix(sizeof(object), object, other, other, 5, 5); });
}
static void CheckSixteenByte() {
alignas(16) Uint128 value = Make(1, ~0ull);
Require(__sync_fetch_and_add_16_nid_postfix(&value, 1) == Make(1, ~0ull) && value == Make(2, 0));
Require(__sync_fetch_and_sub_16_nid_postfix(&value, 1) == Make(2, 0) && value == Make(1, ~0ull));
Require(__sync_fetch_and_and_16_nid_postfix(&value, Make(1, 0xff00)) == Make(1, ~0ull) && value == Make(1, 0xff00));
Require(__sync_fetch_and_or_16_nid_postfix(&value, Make(4, 0xff)) == Make(1, 0xff00) && value == Make(5, 0xffff));
Require(__sync_fetch_and_xor_16_nid_postfix(&value, Make(5, 0xf0f0)) == Make(5, 0xffff) && value == Make(0, 0x0f0f));
Require(__sync_lock_test_and_set_16_nid_postfix(&value, Make(9, 9)) == Make(0, 0x0f0f) && value == Make(9, 9));
}
static void CheckConcurrentAdds() {
alignas(16) Uint128 counter = Make(0, ~0ull - 1000);
constexpr int Iterations = 20000;
auto worker = [&counter] {
for (int i = 0; i < Iterations; ++i) __sync_fetch_and_add_16_nid_postfix(&counter, 1);
};
std::thread first(worker);
std::thread second(worker);
first.join();
second.join();
Require(counter == Make(0, ~0ull - 1000) + 2 * Iterations);
}
int main() {
CheckLockFree();
CheckGenericTraps();
CheckSixteenByte();
CheckConcurrentAdds();
return 0;
}