Files
psxrecomp/runtime/tests/test_pgxp_execution_snapshot.cpp

110 lines
4.5 KiB
C++

#include "pgxp.h"
#include "pgxp_hooks.h"
#include <atomic>
#include <cstdlib>
#include <thread>
#define CHECK(c) do { if (!(c)) std::abort(); } while (0)
extern "C" int gte_geometry_correction_lookup(uint32_t, int32_t*, int32_t*) { return 0; }
extern "C" int gte_geometry_correction_lookup_probe(uint32_t, int32_t*, int32_t*) { return 0; }
extern "C" int psx_mod_gpu_dma_memory_contains(uint32_t, uint32_t) { return 0; }
static constexpr uint32_t address = 0x80001000, packed = 0x000C0008;
static void seed(int32_t x, uint32_t at = address) {
pgxp_gte_push_sxy(x, 12 << 16, 300, packed);
pgxp_store_gte_reg(at, 15);
}
static void check(int32_t x, uint32_t at = address) {
int32_t actual_x, y;
uint16_t z;
CHECK(pgxp_load_precise_word(at, packed, &actual_x, &y, &z));
CHECK(actual_x == x && y == 12 << 16 && z == 300);
}
int main() {
pgxp_set_enabled(1);
pgxp_set_cpu_mode(1);
seed(8 << 16);
PGXPExecutionSnapshot *snapshot = nullptr;
std::thread prepare([&] { snapshot = pgxp_execution_snapshot_create(); });
prepare.join();
CHECK(snapshot && pgxp_execution_snapshot_capture(snapshot));
std::atomic<int> phase{0};
std::thread worker([&] {
CHECK(pgxp_execution_snapshot_bind(snapshot));
check(8 << 16);
seed((8 << 16) + 32768);
check((8 << 16) + 32768);
phase.store(1, std::memory_order_release);
while (phase.load(std::memory_order_acquire) != 2) { }
check((8 << 16) + 32768);
pgxp_execution_snapshot_unbind();
});
while (phase.load(std::memory_order_acquire) != 1) { }
check(8 << 16);
seed((8 << 16) + 16384);
phase.store(2, std::memory_order_release);
worker.join();
check((8 << 16) + 16384);
CHECK(pgxp_execution_snapshot_capture(snapshot));
std::thread next([&] {
CHECK(pgxp_execution_snapshot_bind(snapshot));
check((8 << 16) + 16384);
seed((8 << 16) + 49152);
pgxp_execution_snapshot_unbind();
});
next.join();
/* Re-publication must undo worker writes even if live RAM shadows have
* not changed since the preceding capture. */
CHECK(pgxp_execution_snapshot_capture(snapshot));
std::thread unchanged([&] {
CHECK(pgxp_execution_snapshot_bind(snapshot));
check((8 << 16) + 16384);
pgxp_execution_snapshot_unbind();
});
unchanged.join();
/* Independent consumers retain their publication even after several
* source stores to the same page in one epoch. */
PGXPExecutionSnapshot *other = pgxp_execution_snapshot_create();
CHECK(other);
seed((8 << 16) + 32768);
CHECK(pgxp_execution_snapshot_capture(snapshot));
seed((8 << 16) + 49152);
CHECK(pgxp_execution_snapshot_capture(other));
std::thread consumers([&] {
CHECK(pgxp_execution_snapshot_bind(snapshot)); check((8 << 16) + 32768);
pgxp_execution_snapshot_unbind();
CHECK(pgxp_execution_snapshot_bind(other)); check((8 << 16) + 49152);
pgxp_execution_snapshot_unbind();
});
consumers.join();
/* A load that invalidates precision really does mutate a RAM shadow;
* unlike a matching read, that mutation must reach the next publication. */
psx_pgxp_load(nullptr, (0x23u << 26) | (2u << 16), address, packed ^ 1u);
CHECK(pgxp_execution_snapshot_capture(snapshot));
std::thread invalidated([&] {
CHECK(pgxp_execution_snapshot_bind(snapshot));
int32_t x, y; uint16_t z;
CHECK(!pgxp_load_precise_word(address, packed ^ 1u, &x, &y, &z));
pgxp_execution_snapshot_unbind();
});
invalidated.join();
/* Adjacent words across a publication-page boundary remain independent,
* including worker writes to a page the source has not changed. */
seed(8 << 16, address + 1020); seed(9 << 16, address + 1024);
CHECK(pgxp_execution_snapshot_capture(snapshot));
std::thread boundary([&] {
CHECK(pgxp_execution_snapshot_bind(snapshot));
check(8 << 16, address + 1020); check(9 << 16, address + 1024);
seed(10 << 16, address + 1020); seed(11 << 16, address + 1024);
pgxp_execution_snapshot_unbind();
}); boundary.join();
seed(12 << 16, address + 1024);
CHECK(pgxp_execution_snapshot_capture(snapshot));
std::thread recapture([&] {
CHECK(pgxp_execution_snapshot_bind(snapshot));
check(8 << 16, address + 1020); check(12 << 16, address + 1024);
pgxp_execution_snapshot_unbind();
}); recapture.join();
pgxp_execution_snapshot_destroy(other);
pgxp_execution_snapshot_destroy(snapshot);
return 0;
}