atomic cas use prefetchw if available
remove useless memorybarrier remove double membarrier in wait pm4 cmd add int64 cvar use int64 cvar for x64 feature mask Rework some functions that were frontend bound according to vtune placing some of their code in different noinline functions, profiling after indicating l1 cache misses decreased and perf of func increased remove long vpinsrd dep chain code for conversion.h, instead do normal load+bswap or movbe if avail Much faster entry table via split_map, code size could be improved though GetResolveInfo was very large and had impact on icache, mark callees as noinline + msvc pragma optimize small use log2 shifts instead of integer divides in memory minor optimizations in PhysicalHeap::EnableAccessCallbacks, the majority of time in the function is spent looping, NOT calling Protect! Someone should optimize this function and rework the algo completely remove wonky scheduling log message, it was spammy and unhelpful lock count was unnecessary for criticalsection mutex, criticalsection is already a recursive mutex brief notes i gotta run
This commit is contained in:
@@ -335,7 +335,8 @@ ICommandVar* define_cmdvar(const char* name, T* default_value,
|
||||
|
||||
#define DEFINE_uint64(name, default_value, description, category) \
|
||||
DEFINE_CVar(name, default_value, description, category, false, uint64_t)
|
||||
|
||||
#define DEFINE_int64(name, default_value, description, category) \
|
||||
DEFINE_CVar(name, default_value, description, category, false, int64_t)
|
||||
#define DEFINE_double(name, default_value, description, category) \
|
||||
DEFINE_CVar(name, default_value, description, category, false, double)
|
||||
|
||||
@@ -383,7 +384,7 @@ ICommandVar* define_cmdvar(const char* name, T* default_value,
|
||||
#define DECLARE_uint32(name) DECLARE_CVar(name, uint32_t)
|
||||
|
||||
#define DECLARE_uint64(name) DECLARE_CVar(name, uint64_t)
|
||||
|
||||
#define DECLARE_int64(name) DECLARE_CVar(name, int64_t)
|
||||
#define DECLARE_double(name) DECLARE_CVar(name, double)
|
||||
|
||||
#define DECLARE_string(name) DECLARE_CVar(name, std::string)
|
||||
|
||||
@@ -26,7 +26,7 @@ check this and release the mutex one way to do this is by using FlsAlloc and
|
||||
PFLS_CALLBACK_FUNCTION, which gets called with the fiber local data when a
|
||||
thread exits
|
||||
*/
|
||||
thread_local unsigned global_mutex_depth = 0;
|
||||
|
||||
static CRITICAL_SECTION* global_critical_section(xe_global_mutex* mutex) {
|
||||
return reinterpret_cast<CRITICAL_SECTION*>(mutex);
|
||||
}
|
||||
@@ -38,29 +38,16 @@ xe_global_mutex::xe_global_mutex() {
|
||||
xe_global_mutex ::~xe_global_mutex() {
|
||||
DeleteCriticalSection(global_critical_section(this));
|
||||
}
|
||||
|
||||
void xe_global_mutex::lock() {
|
||||
if (global_mutex_depth) {
|
||||
} else {
|
||||
EnterCriticalSection(global_critical_section(this));
|
||||
}
|
||||
global_mutex_depth++;
|
||||
EnterCriticalSection(global_critical_section(this));
|
||||
}
|
||||
void xe_global_mutex::unlock() {
|
||||
if (--global_mutex_depth == 0) {
|
||||
LeaveCriticalSection(global_critical_section(this));
|
||||
}
|
||||
LeaveCriticalSection(global_critical_section(this));
|
||||
}
|
||||
bool xe_global_mutex::try_lock() {
|
||||
if (global_mutex_depth) {
|
||||
++global_mutex_depth;
|
||||
return true;
|
||||
} else {
|
||||
BOOL success = TryEnterCriticalSection(global_critical_section(this));
|
||||
if (success) {
|
||||
++global_mutex_depth;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
BOOL success = TryEnterCriticalSection(global_critical_section(this));
|
||||
return success;
|
||||
}
|
||||
|
||||
CRITICAL_SECTION* fast_crit(xe_fast_mutex* mutex) {
|
||||
|
||||
@@ -116,15 +116,15 @@
|
||||
#define XE_LIKELY(...) (!!(__VA_ARGS__))
|
||||
#define XE_UNLIKELY(...) (!!(__VA_ARGS__))
|
||||
#define XE_MSVC_ASSUME(...) __assume(__VA_ARGS__)
|
||||
#define XE_NOALIAS __declspec(noalias)
|
||||
#define XE_NOALIAS __declspec(noalias)
|
||||
#elif XE_COMPILER_HAS_GNU_EXTENSIONS == 1
|
||||
#define XE_FORCEINLINE __attribute__((always_inline))
|
||||
#define XE_NOINLINE __attribute__((noinline))
|
||||
#define XE_COLD __attribute__((cold))
|
||||
#define XE_LIKELY(...) __builtin_expect(!!(__VA_ARGS__), true)
|
||||
#define XE_UNLIKELY(...) __builtin_expect(!!(__VA_ARGS__), false)
|
||||
#define XE_NOALIAS
|
||||
//cant do unevaluated assume
|
||||
#define XE_NOALIAS
|
||||
// cant do unevaluated assume
|
||||
#define XE_MSVC_ASSUME(...) static_cast<void>(0)
|
||||
#else
|
||||
#define XE_FORCEINLINE inline
|
||||
@@ -137,7 +137,13 @@
|
||||
#define XE_MSVC_ASSUME(...) static_cast<void>(0)
|
||||
|
||||
#endif
|
||||
|
||||
#if XE_COMPILER_HAS_MSVC_EXTENSIONS == 1
|
||||
#define XE_MSVC_OPTIMIZE_SMALL() __pragma(optimize("s", on))
|
||||
#define XE_MSVC_OPTIMIZE_REVERT() __pragma(optimize("", on))
|
||||
#else
|
||||
#define XE_MSVC_OPTIMIZE_SMALL()
|
||||
#define XE_MSVC_OPTIMIZE_REVERT()
|
||||
#endif
|
||||
#if XE_COMPILER_HAS_GNU_EXTENSIONS == 1
|
||||
#define XE_LIKELY_IF(...) if (XE_LIKELY(__VA_ARGS__))
|
||||
#define XE_UNLIKELY_IF(...) if (XE_UNLIKELY(__VA_ARGS__))
|
||||
@@ -180,7 +186,7 @@ const char kPathSeparator = '/';
|
||||
const char kGuestPathSeparator = '\\';
|
||||
|
||||
} // namespace xe
|
||||
#if XE_ARCH_AMD64==1
|
||||
#if XE_ARCH_AMD64 == 1
|
||||
#include "platform_amd64.h"
|
||||
#endif
|
||||
#endif // XENIA_BASE_PLATFORM_H_
|
||||
|
||||
@@ -7,13 +7,12 @@
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
#include "third_party/xbyak/xbyak/xbyak.h"
|
||||
#include "third_party/xbyak/xbyak/xbyak_util.h"
|
||||
DEFINE_int32(x64_extension_mask, -1,
|
||||
DEFINE_int64(x64_extension_mask, -1LL,
|
||||
"Allow the detection and utilization of specific instruction set "
|
||||
"features.\n"
|
||||
" 0 = x86_64 + AVX1\n"
|
||||
@@ -33,79 +32,92 @@ DEFINE_int32(x64_extension_mask, -1,
|
||||
"x64");
|
||||
namespace xe {
|
||||
namespace amd64 {
|
||||
static uint32_t g_feature_flags = 0U;
|
||||
static uint64_t g_feature_flags = 0U;
|
||||
static bool g_did_initialize_feature_flags = false;
|
||||
uint32_t GetFeatureFlags() {
|
||||
xenia_assert(g_did_initialize_feature_flags);
|
||||
return g_feature_flags;
|
||||
uint64_t GetFeatureFlags() {
|
||||
xenia_assert(g_did_initialize_feature_flags);
|
||||
return g_feature_flags;
|
||||
}
|
||||
XE_COLD
|
||||
XE_NOINLINE
|
||||
void InitFeatureFlags() {
|
||||
uint32_t feature_flags_ = 0U;
|
||||
|
||||
Xbyak::util::Cpu cpu_;
|
||||
uint64_t feature_flags_ = 0U;
|
||||
{
|
||||
Xbyak::util::Cpu cpu_;
|
||||
#define TEST_EMIT_FEATURE(emit, ext) \
|
||||
if ((cvars::x64_extension_mask & emit) == emit) { \
|
||||
feature_flags_ |= (cpu_.has(ext) ? emit : 0); \
|
||||
}
|
||||
|
||||
TEST_EMIT_FEATURE(kX64EmitAVX2, Xbyak::util::Cpu::tAVX2);
|
||||
TEST_EMIT_FEATURE(kX64EmitFMA, Xbyak::util::Cpu::tFMA);
|
||||
TEST_EMIT_FEATURE(kX64EmitLZCNT, Xbyak::util::Cpu::tLZCNT);
|
||||
TEST_EMIT_FEATURE(kX64EmitBMI1, Xbyak::util::Cpu::tBMI1);
|
||||
TEST_EMIT_FEATURE(kX64EmitBMI2, Xbyak::util::Cpu::tBMI2);
|
||||
TEST_EMIT_FEATURE(kX64EmitMovbe, Xbyak::util::Cpu::tMOVBE);
|
||||
TEST_EMIT_FEATURE(kX64EmitGFNI, Xbyak::util::Cpu::tGFNI);
|
||||
TEST_EMIT_FEATURE(kX64EmitAVX512F, Xbyak::util::Cpu::tAVX512F);
|
||||
TEST_EMIT_FEATURE(kX64EmitAVX512VL, Xbyak::util::Cpu::tAVX512VL);
|
||||
TEST_EMIT_FEATURE(kX64EmitAVX512BW, Xbyak::util::Cpu::tAVX512BW);
|
||||
TEST_EMIT_FEATURE(kX64EmitAVX512DQ, Xbyak::util::Cpu::tAVX512DQ);
|
||||
TEST_EMIT_FEATURE(kX64EmitAVX512VBMI, Xbyak::util::Cpu::tAVX512VBMI);
|
||||
TEST_EMIT_FEATURE(kX64EmitPrefetchW, Xbyak::util::Cpu::tPREFETCHW);
|
||||
TEST_EMIT_FEATURE(kX64EmitAVX2, Xbyak::util::Cpu::tAVX2);
|
||||
TEST_EMIT_FEATURE(kX64EmitFMA, Xbyak::util::Cpu::tFMA);
|
||||
TEST_EMIT_FEATURE(kX64EmitLZCNT, Xbyak::util::Cpu::tLZCNT);
|
||||
TEST_EMIT_FEATURE(kX64EmitBMI1, Xbyak::util::Cpu::tBMI1);
|
||||
TEST_EMIT_FEATURE(kX64EmitBMI2, Xbyak::util::Cpu::tBMI2);
|
||||
TEST_EMIT_FEATURE(kX64EmitMovbe, Xbyak::util::Cpu::tMOVBE);
|
||||
TEST_EMIT_FEATURE(kX64EmitGFNI, Xbyak::util::Cpu::tGFNI);
|
||||
TEST_EMIT_FEATURE(kX64EmitAVX512F, Xbyak::util::Cpu::tAVX512F);
|
||||
TEST_EMIT_FEATURE(kX64EmitAVX512VL, Xbyak::util::Cpu::tAVX512VL);
|
||||
TEST_EMIT_FEATURE(kX64EmitAVX512BW, Xbyak::util::Cpu::tAVX512BW);
|
||||
TEST_EMIT_FEATURE(kX64EmitAVX512DQ, Xbyak::util::Cpu::tAVX512DQ);
|
||||
TEST_EMIT_FEATURE(kX64EmitAVX512VBMI, Xbyak::util::Cpu::tAVX512VBMI);
|
||||
TEST_EMIT_FEATURE(kX64EmitPrefetchW, Xbyak::util::Cpu::tPREFETCHW);
|
||||
#undef TEST_EMIT_FEATURE
|
||||
/*
|
||||
fix for xbyak bug/omission, amd cpus are never checked for lzcnt. fixed in
|
||||
latest version of xbyak
|
||||
*/
|
||||
unsigned int data[4];
|
||||
Xbyak::util::Cpu::getCpuid(0x80000001, data);
|
||||
unsigned amd_flags = data[2];
|
||||
if (amd_flags & (1U << 5)) {
|
||||
if ((cvars::x64_extension_mask & kX64EmitLZCNT) == kX64EmitLZCNT) {
|
||||
feature_flags_ |= kX64EmitLZCNT;
|
||||
}
|
||||
}
|
||||
// todo: although not reported by cpuid, zen 1 and zen+ also have fma4
|
||||
if (amd_flags & (1U << 16)) {
|
||||
if ((cvars::x64_extension_mask & kX64EmitFMA4) == kX64EmitFMA4) {
|
||||
feature_flags_ |= kX64EmitFMA4;
|
||||
}
|
||||
}
|
||||
if (amd_flags & (1U << 21)) {
|
||||
if ((cvars::x64_extension_mask & kX64EmitTBM) == kX64EmitTBM) {
|
||||
feature_flags_ |= kX64EmitTBM;
|
||||
}
|
||||
}
|
||||
if (amd_flags & (1U << 11)) {
|
||||
if ((cvars::x64_extension_mask & kX64EmitXOP) == kX64EmitXOP) {
|
||||
feature_flags_ |= kX64EmitXOP;
|
||||
}
|
||||
}
|
||||
if (cpu_.has(Xbyak::util::Cpu::tAMD)) {
|
||||
bool is_zennish = cpu_.displayFamily >= 0x17;
|
||||
/*
|
||||
chrispy: according to agner's tables, all amd architectures that
|
||||
we support (ones with avx) have the same timings for
|
||||
jrcxz/loop/loope/loopne as for other jmps
|
||||
*/
|
||||
feature_flags_ |= kX64FastJrcx;
|
||||
feature_flags_ |= kX64FastLoop;
|
||||
if (is_zennish) {
|
||||
// ik that i heard somewhere that this is the case for zen, but i need to
|
||||
// verify. cant find my original source for that.
|
||||
// todo: ask agner?
|
||||
feature_flags_ |= kX64FlagsIndependentVars;
|
||||
fix for xbyak bug/omission, amd cpus are never checked for lzcnt. fixed in
|
||||
latest version of xbyak
|
||||
*/
|
||||
unsigned int data[4];
|
||||
Xbyak::util::Cpu::getCpuid(0x80000001, data);
|
||||
unsigned amd_flags = data[2];
|
||||
if (amd_flags & (1U << 5)) {
|
||||
if ((cvars::x64_extension_mask & kX64EmitLZCNT) == kX64EmitLZCNT) {
|
||||
feature_flags_ |= kX64EmitLZCNT;
|
||||
}
|
||||
}
|
||||
// todo: although not reported by cpuid, zen 1 and zen+ also have fma4
|
||||
if (amd_flags & (1U << 16)) {
|
||||
if ((cvars::x64_extension_mask & kX64EmitFMA4) == kX64EmitFMA4) {
|
||||
feature_flags_ |= kX64EmitFMA4;
|
||||
}
|
||||
}
|
||||
if (amd_flags & (1U << 21)) {
|
||||
if ((cvars::x64_extension_mask & kX64EmitTBM) == kX64EmitTBM) {
|
||||
feature_flags_ |= kX64EmitTBM;
|
||||
}
|
||||
}
|
||||
if (amd_flags & (1U << 11)) {
|
||||
if ((cvars::x64_extension_mask & kX64EmitXOP) == kX64EmitXOP) {
|
||||
feature_flags_ |= kX64EmitXOP;
|
||||
}
|
||||
}
|
||||
if (cpu_.has(Xbyak::util::Cpu::tAMD)) {
|
||||
bool is_zennish = cpu_.displayFamily >= 0x17;
|
||||
/*
|
||||
chrispy: according to agner's tables, all amd architectures
|
||||
that we support (ones with avx) have the same timings for
|
||||
jrcxz/loop/loope/loopne as for other jmps
|
||||
*/
|
||||
feature_flags_ |= kX64FastJrcx;
|
||||
feature_flags_ |= kX64FastLoop;
|
||||
if (is_zennish) {
|
||||
// ik that i heard somewhere that this is the case for zen, but i need
|
||||
// to verify. cant find my original source for that. todo: ask agner?
|
||||
feature_flags_ |= kX64FlagsIndependentVars;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
unsigned int data[4];
|
||||
memset(data, 0, sizeof(data));
|
||||
// intel extended features
|
||||
Xbyak::util::Cpu::getCpuidEx(7, 0, data);
|
||||
if ((data[2] & (1 << 28)) &&
|
||||
(cvars::x64_extension_mask & kX64EmitMovdir64M)) {
|
||||
feature_flags_ |= kX64EmitMovdir64M;
|
||||
}
|
||||
if ((data[1] & (1 << 9)) && (cvars::x64_extension_mask & kX64FastRepMovs)) {
|
||||
feature_flags_ |= kX64FastRepMovs;
|
||||
}
|
||||
}
|
||||
g_feature_flags = feature_flags_;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
namespace xe {
|
||||
namespace amd64 {
|
||||
enum X64FeatureFlags {
|
||||
enum X64FeatureFlags : uint64_t {
|
||||
kX64EmitAVX2 = 1 << 0,
|
||||
kX64EmitFMA = 1 << 1,
|
||||
kX64EmitLZCNT = 1 << 2, // this is actually ABM and includes popcount
|
||||
@@ -44,14 +44,13 @@ enum X64FeatureFlags {
|
||||
// instructions, and FX users need the boost
|
||||
kX64EmitFMA4 = 1 << 17, // todo: also use on zen1?
|
||||
kX64EmitTBM = 1 << 18,
|
||||
// kX64XMMRegisterMergeOptimization = 1 << 19, //section 2.11.5, amd family
|
||||
// 17h/19h optimization manuals. allows us to save 1 byte on certain xmm
|
||||
// instructions by using the legacy sse version if we recently cleared the
|
||||
// high 128 bits of the
|
||||
kX64EmitMovdir64M = 1 << 19,
|
||||
kX64FastRepMovs = 1 << 20
|
||||
|
||||
};
|
||||
|
||||
XE_NOALIAS
|
||||
uint32_t GetFeatureFlags();
|
||||
uint64_t GetFeatureFlags();
|
||||
XE_COLD
|
||||
void InitFeatureFlags();
|
||||
|
||||
|
||||
@@ -299,6 +299,12 @@ class Event : public WaitHandle {
|
||||
// the nonsignaled state after releasing the appropriate number of waiting
|
||||
// threads.
|
||||
virtual void Pulse() = 0;
|
||||
#if XE_PLATFORM_WIN32 ==1
|
||||
//SetEvent, but if there is a waiter we immediately transfer execution to it
|
||||
virtual void SetBoostPriority() = 0;
|
||||
#else
|
||||
void SetBoostPriority() { Set() }
|
||||
#endif
|
||||
};
|
||||
|
||||
// Models a Win32-like semaphore object.
|
||||
|
||||
@@ -39,6 +39,8 @@ XE_NTDLL_IMPORT(NtWaitForSingleObject, cls_NtWaitForSingleObject,
|
||||
NtWaitForSingleObjectPointer);
|
||||
|
||||
XE_NTDLL_IMPORT(NtSetEvent, cls_NtSetEvent, NtSetEventPointer);
|
||||
XE_NTDLL_IMPORT(NtSetEventBoostPriority, cls_NtSetEventBoostPriority,
|
||||
NtSetEventBoostPriorityPointer);
|
||||
// difference between NtClearEvent and NtResetEvent is that NtResetEvent returns
|
||||
// the events state prior to the call, but we dont need that. might need to
|
||||
// check whether one or the other is faster in the kernel though yeah, just
|
||||
@@ -53,6 +55,7 @@ XE_NTDLL_IMPORT(NtReleaseSemaphore, cls_NtReleaseSemaphore,
|
||||
|
||||
XE_NTDLL_IMPORT(NtDelayExecution, cls_NtDelayExecution,
|
||||
NtDelayExecutionPointer);
|
||||
|
||||
namespace xe {
|
||||
namespace threading {
|
||||
|
||||
@@ -137,7 +140,7 @@ void MaybeYield() {
|
||||
#endif
|
||||
#endif
|
||||
// memorybarrier is really not necessary here...
|
||||
MemoryBarrier();
|
||||
// MemoryBarrier();
|
||||
}
|
||||
|
||||
void SyncMemory() { MemoryBarrier(); }
|
||||
@@ -288,11 +291,19 @@ class Win32Event : public Win32Handle<Event> {
|
||||
void Set() override { NtSetEventPointer.invoke(handle_, nullptr); }
|
||||
void Reset() override { NtClearEventPointer.invoke(handle_); }
|
||||
void Pulse() override { NtPulseEventPointer.invoke(handle_, nullptr); }
|
||||
void SetBoostPriority() override {
|
||||
// no previous state for boostpriority
|
||||
NtSetEventBoostPriorityPointer.invoke(handle_);
|
||||
}
|
||||
#else
|
||||
void Set() override { SetEvent(handle_); }
|
||||
void Reset() override { ResetEvent(handle_); }
|
||||
void Pulse() override { PulseEvent(handle_); }
|
||||
|
||||
void SetBoostPriority() override {
|
||||
// no win32 version of boostpriority
|
||||
SetEvent(handle_);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user