Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,12 @@
#include <cstdint>
#include <cstdlib>
#include <thread>
#if defined(__x86_64__) || defined(_M_X64)
#include <immintrin.h>
#endif
#include "prx/libc/include/PreciseWait.hpp"

namespace AgcDriver {

inline void SpinPause() {
#if defined(__x86_64__) || defined(_M_X64)
_mm_pause();
#endif
PreciseSpinPause();
}

inline unsigned UsableProcessors() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#ifdef _WIN32
#include <windows.h>
#endif
#include <immintrin.h>
#include "prx/libc/include/PreciseWait.hpp"
#include <algorithm>
#include <tuple>
#include <vector>
Expand Down Expand Up @@ -366,7 +366,7 @@ void Driver::waitMemory(std::span<const std::uint32_t> packet, std::uint32_t que
}
++polls;
if (spinning) {
_mm_pause();
PreciseSpinPause();
if ((polls & 63u) != 0) continue;
if (std::chrono::steady_clock::now() - spinStart > spinLimit) spinning = false;
} else if (pauseSpin || polls >= 4000) {
Expand Down
14 changes: 12 additions & 2 deletions core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
#include <atomic>
#include <algorithm>
#include <bit>
#if defined(__x86_64__) || defined(_M_X64)
#include <emmintrin.h>
#if defined(_MSC_VER) && !defined(__clang__)
#include <intrin.h>
#else
#include <cpuid.h>
#endif
#endif
#include <functional>
#include <cstdio>
#include <cstdlib>
Expand Down Expand Up @@ -3057,12 +3059,15 @@ void WriteChangedCommitted(std::uint64_t address, std::span<const std::byte> cur
}
}

#if defined(__x86_64__) || defined(_M_X64)
static std::uint32_t changedByteCount(__m128i same) {
const auto sums = _mm_sad_epu8(_mm_and_si128(same, _mm_set1_epi8(1)), _mm_setzero_si128());
return 16u - static_cast<std::uint32_t>(_mm_cvtsi128_si32(_mm_add_epi32(sums, _mm_unpackhi_epi64(sums, sums))));
}
#endif

std::uint32_t ChangedByteStoreLimit() {
#if defined(__x86_64__) || defined(_M_X64)
static const auto limit = [] {
#if defined(_MSC_VER) && !defined(__clang__)
int registers[4]{};
Expand All @@ -3075,13 +3080,16 @@ std::uint32_t ChangedByteStoreLimit() {
return ebx == 0x68747541u && edx == 0x69746e65u && ecx == 0x444d4163u ? 16u : 4u;
}();
return limit;
#else
return 16u;
#endif
}

void WriteChanged(std::uint64_t address, std::span<const std::byte> current, std::span<const std::byte> original) {
WriteChanged(address, current, original, ChangedByteStoreLimit());
}

static void writeChanged(std::uint64_t address, std::span<const std::byte> current, std::span<const std::byte> original, std::uint32_t byteStoreLimit, bool keepGuest) {
static void writeChanged(std::uint64_t address, std::span<const std::byte> current, std::span<const std::byte> original, [[maybe_unused]] std::uint32_t byteStoreLimit, bool keepGuest) {
require(current.size() == original.size(), "write-back snapshot sizes differ");
BreakProofs();
if (current.empty()) return;
Expand All @@ -3097,8 +3105,9 @@ static void writeChanged(std::uint64_t address, std::span<const std::byte> curre
CheckRange(destination, size, 1, true);
std::size_t firstChanged = size;
std::size_t lastChanged = 0;
bool masked = false;
std::size_t at = 0;
#if defined(__x86_64__) || defined(_M_X64)
bool masked = false;
for (; at + 16 <= size; at += 16) {
const auto bytes = _mm_loadu_si128(reinterpret_cast<const __m128i*>(current.data() + at));
const auto uploaded = _mm_loadu_si128(reinterpret_cast<const __m128i*>(original.data() + at));
Expand All @@ -3121,6 +3130,7 @@ static void writeChanged(std::uint64_t address, std::span<const std::byte> curre
lastChanged = std::max(lastChanged, at + 32 - static_cast<std::size_t>(std::countl_zero(changed)));
}
if (masked) _mm_sfence();
#endif
for (; at < size; ++at) {
if (current[at] == original[at] || (keepGuest && destination[at] != original[at])) continue;
destination[at] = current[at];
Expand Down
8 changes: 2 additions & 6 deletions core/libs/prx/libSceAgcDriver/Graphics/include/SlimMutex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@

#include <atomic>
#include <thread>
#if defined(__x86_64__) || defined(_M_X64)
#include <immintrin.h>
#endif
#include "prx/libc/include/PreciseWait.hpp"

namespace AgcDriver::Graphics {

Expand All @@ -21,9 +19,7 @@ class SlimMutex {
while (locked.load(std::memory_order_relaxed)) {
if (spins < 128) {
++spins;
#if defined(__x86_64__) || defined(_M_X64)
_mm_pause();
#endif
PreciseSpinPause();
} else {
std::this_thread::yield();
}
Expand Down
13 changes: 7 additions & 6 deletions core/libs/prx/libSceJobManager/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <immintrin.h>
#include "prx/libc/include/GuestCpu.hpp"
#include "prx/libc/include/PreciseWait.hpp"
#include <memory>
#include <mutex>
#include <span>
Expand Down Expand Up @@ -821,26 +822,26 @@ constexpr std::uint32_t SpinPauses = 1024;
void* APS5_VABI WorkerEntry(void* argument) {
Manager& manager = *static_cast<Manager*>(argument);
currentWorker = &manager;
std::uint64_t lastWork = __rdtsc();
std::uint64_t lastWork = GuestCpu::ReadTsc();
std::unique_lock guard(manager.lock);
while (manager.running) {
if (RunOne(manager, guard)) {
lastWork = __rdtsc();
lastWork = GuestCpu::ReadTsc();
continue;
}
const bool spinning = manager.pendingInvocations != 0 && __rdtsc() - lastWork < manager.spinTicks;
const bool spinning = manager.pendingInvocations != 0 && GuestCpu::ReadTsc() - lastWork < manager.spinTicks;
if (spinning) {
const std::uint32_t epoch = manager.workEpoch.load(std::memory_order_acquire);
guard.unlock();
for (std::uint32_t pause = 0; pause != SpinPauses && manager.workEpoch.load(std::memory_order_acquire) == epoch; ++pause) _mm_pause();
for (std::uint32_t pause = 0; pause != SpinPauses && manager.workEpoch.load(std::memory_order_acquire) == epoch; ++pause) PreciseSpinPause();
guard.lock();
continue;
}
if (manager.sleepingWorkers >= manager.workerCount - manager.persistentThreads) {
const std::uint32_t epoch = manager.workEpoch.load(std::memory_order_acquire);
guard.unlock();
while (manager.workEpoch.load(std::memory_order_acquire) == epoch) {
for (std::uint32_t pause = 0; pause != SpinPauses; ++pause) _mm_pause();
for (std::uint32_t pause = 0; pause != SpinPauses; ++pause) PreciseSpinPause();
}
guard.lock();
continue;
Expand Down
88 changes: 88 additions & 0 deletions core/libs/prx/libc/include/GuestCpu.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#ifndef CORE_LIBS_PRX_LIBC_INCLUDE_GUESTCPU_HPP
#define CORE_LIBS_PRX_LIBC_INCLUDE_GUESTCPU_HPP

#include <array>
#include <cstdint>

#if defined(__x86_64__)

#include <x86intrin.h>
#include <xmmintrin.h>

namespace GuestCpu {

inline std::uint64_t ReadTsc() { return __rdtsc(); }

inline std::array<std::uint32_t, 4> Cpuid(const std::uint32_t leaf, const std::uint32_t subleaf) {
std::uint32_t eax;
std::uint32_t ebx;
std::uint32_t ecx;
std::uint32_t edx;
asm volatile("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "0"(leaf), "2"(subleaf));
return {eax, ebx, ecx, edx};
}

inline std::uint64_t Xgetbv(const std::uint32_t index) {
std::uint32_t low;
std::uint32_t high;
asm volatile("xgetbv" : "=a"(low), "=d"(high) : "c"(index));
return (static_cast<std::uint64_t>(high) << 32) | low;
}

inline std::uint32_t Mxcsr() { return _mm_getcsr(); }
inline void SetMxcsr(const std::uint32_t value) { _mm_setcsr(value); }

inline std::uint16_t X87Control() {
std::uint16_t control;
asm volatile("fnstcw %0" : "=m"(control));
return control;
}

inline void SetX87Control(const std::uint16_t value) {
asm volatile("fldcw %0" : : "m"(value));
}

inline std::uint16_t X87Status() {
std::uint16_t status;
asm volatile("fnstsw %0" : "=am"(status));
return status;
}

inline void SetX87Status(const std::uint16_t value) {
std::array<std::uint32_t, 7> environment;
asm volatile("fnstenv %0" : "=m"(environment));
environment[1] = (environment[1] & 0xffff0000u) | value;
asm volatile("fldenv %0" : : "m"(environment));
}

inline void X87ClearExceptions() { asm volatile("fnclex"); }
inline void X87Wait() { asm volatile("fwait"); }

inline std::uint32_t MxcsrMask() {
alignas(16) std::array<std::uint32_t, 128> state;
asm volatile("fxsave %0" : "=m"(state));
return state[7] != 0 ? state[7] : 0xffbfu;
}

inline std::array<std::uint32_t, 7> X87Environment() {
std::array<std::uint32_t, 7> environment;
asm volatile("fnstenv %0\n\tfldcw %0" : "+m"(environment));
return environment;
}

inline void SetX87Environment(const std::array<std::uint32_t, 7>& environment) {
asm volatile("fldenv %0" : : "m"(environment) : "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)");
}

inline void RaiseInvalidForSignalingNan(const float value) {
volatile int unordered = _mm_ucomieq_ss(_mm_set_ss(value), _mm_setzero_ps());
(void)unordered;
}

}

#else
#error "GuestCpu.hpp supports only x86-64 targets"
#endif

#endif
26 changes: 14 additions & 12 deletions core/libs/prx/libc/include/PreciseWait.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ inline constexpr std::uint64_t PrecisePauseThresholdTicks(std::uint64_t freq) {
return PreciseHundredNanosToQpcTicks(2ULL, freq);
}

#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
#include <immintrin.h>
#endif

inline void PreciseSpinPause() {
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
_mm_pause();
#elif defined(__aarch64__)
asm volatile("yield" ::: "memory");
#else
std::atomic_signal_fence(std::memory_order_seq_cst);
#endif
}

#ifdef _WIN32
#include <windows.h>
#include "prx/libc/include/HostThreadLocal.hpp"
Expand Down Expand Up @@ -119,18 +133,6 @@ inline void PreciseSleepHundredNanos(unsigned long long hundredNanos) {
#ifdef __linux__
#include <sys/prctl.h>
#endif
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
#include <immintrin.h>
#endif

inline void PreciseSpinPause() {
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
_mm_pause();
#else
std::atomic_signal_fence(std::memory_order_seq_cst);
#endif
}

inline std::uint64_t PreciseMonotonicNanos() {
struct timespec ts;
#if defined(__APPLE__) && defined(_LIBCPP_VERSION) && defined(CLOCK_MONOTONIC_RAW)
Expand Down
Loading
Loading