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:
@@ -23,7 +23,7 @@
|
||||
#define XE_X64_PROFILER_AVAILABLE 1
|
||||
#endif
|
||||
|
||||
DECLARE_int32(x64_extension_mask);
|
||||
DECLARE_int64(x64_extension_mask);
|
||||
|
||||
namespace xe {
|
||||
class Exception;
|
||||
|
||||
@@ -103,74 +103,9 @@ X64Emitter::X64Emitter(X64Backend* backend, XbyakAllocator* allocator)
|
||||
"FAQ for system requirements at https://xenia.jp");
|
||||
return;
|
||||
}
|
||||
#if 1
|
||||
feature_flags_ = amd64::GetFeatureFlags();
|
||||
#else
|
||||
#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);
|
||||
#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;
|
||||
XELOGCPU("Cpu support XOP!\n\n");
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
feature_flags_ = amd64::GetFeatureFlags();
|
||||
|
||||
may_use_membase32_as_zero_reg_ =
|
||||
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(
|
||||
processor()->memory()->virtual_membase())) == 0;
|
||||
|
||||
@@ -299,7 +299,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
void* FindWordConstantOffset(unsigned wordvalue);
|
||||
void* FindDwordConstantOffset(unsigned bytevalue);
|
||||
void* FindQwordConstantOffset(uint64_t bytevalue);
|
||||
bool IsFeatureEnabled(uint32_t feature_flag) const {
|
||||
bool IsFeatureEnabled(uint64_t feature_flag) const {
|
||||
return (feature_flags_ & feature_flag) == feature_flag;
|
||||
}
|
||||
|
||||
@@ -395,7 +395,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
||||
XbyakAllocator* allocator_ = nullptr;
|
||||
XexModule* guest_module_ = nullptr;
|
||||
Xbyak::util::Cpu cpu_;
|
||||
uint32_t feature_flags_ = 0;
|
||||
uint64_t feature_flags_ = 0;
|
||||
uint32_t current_guest_function_ = 0;
|
||||
Xbyak::Label* epilog_label_ = nullptr;
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
#include "xenia/cpu/backend/x64/x64_stack_layout.h"
|
||||
#include "xenia/cpu/hir/hir_builder.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
|
||||
XE_MSVC_OPTIMIZE_SMALL()
|
||||
DEFINE_bool(use_fast_dot_product, false,
|
||||
"Experimental optimization, much shorter sequence on dot products, "
|
||||
"treating inf as overflow instead of using mcxsr"
|
||||
|
||||
@@ -19,16 +19,19 @@ EntryTable::EntryTable() = default;
|
||||
|
||||
EntryTable::~EntryTable() {
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
for (auto it : map_) {
|
||||
Entry* entry = it.second;
|
||||
for (auto it : map_.Values()) {
|
||||
Entry* entry = it;
|
||||
delete entry;
|
||||
}
|
||||
}
|
||||
|
||||
Entry* EntryTable::Get(uint32_t address) {
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
const auto& it = map_.find(address);
|
||||
Entry* entry = it != map_.end() ? it->second : nullptr;
|
||||
uint32_t idx = map_.IndexForKey(address);
|
||||
if (idx == map_.size() || *map_.KeyAt(idx) != address) {
|
||||
return nullptr;
|
||||
}
|
||||
Entry* entry = *map_.ValueAt(idx);
|
||||
if (entry) {
|
||||
// TODO(benvanik): wait if needed?
|
||||
if (entry->status != Entry::STATUS_READY) {
|
||||
@@ -43,8 +46,12 @@ Entry::Status EntryTable::GetOrCreate(uint32_t address, Entry** out_entry) {
|
||||
// https://github.com/facebook/folly/blob/master/folly/AtomicHashMap.h
|
||||
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
const auto& it = map_.find(address);
|
||||
Entry* entry = it != map_.end() ? it->second : nullptr;
|
||||
|
||||
uint32_t idx = map_.IndexForKey(address);
|
||||
|
||||
Entry* entry = idx != map_.size() && *map_.KeyAt(idx) == address
|
||||
? *map_.ValueAt(idx)
|
||||
: nullptr;
|
||||
Entry::Status status;
|
||||
if (entry) {
|
||||
// If we aren't ready yet spin and wait.
|
||||
@@ -65,7 +72,8 @@ Entry::Status EntryTable::GetOrCreate(uint32_t address, Entry** out_entry) {
|
||||
entry->end_address = 0;
|
||||
entry->status = Entry::STATUS_COMPILING;
|
||||
entry->function = 0;
|
||||
map_[address] = entry;
|
||||
map_.InsertAt(address, entry, idx);
|
||||
// map_[address] = entry;
|
||||
status = Entry::STATUS_NEW;
|
||||
}
|
||||
global_lock.unlock();
|
||||
@@ -75,18 +83,18 @@ Entry::Status EntryTable::GetOrCreate(uint32_t address, Entry** out_entry) {
|
||||
|
||||
void EntryTable::Delete(uint32_t address) {
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
const auto itr = map_.find(address);
|
||||
|
||||
if (itr != map_.cend()) {
|
||||
map_.erase(itr);
|
||||
// doesnt this leak memory by not deleting the entry?
|
||||
uint32_t idx = map_.IndexForKey(address);
|
||||
if (idx != map_.size() && *map_.KeyAt(idx) == address) {
|
||||
map_.EraseAt(idx);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Function*> EntryTable::FindWithAddress(uint32_t address) {
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
std::vector<Function*> fns;
|
||||
for (auto& it : map_) {
|
||||
Entry* entry = it.second;
|
||||
for (auto& it : map_.Values()) {
|
||||
Entry* entry = it;
|
||||
if (address >= entry->address && address <= entry->end_address) {
|
||||
if (entry->status == Entry::STATUS_READY) {
|
||||
fns.push_back(entry->function);
|
||||
@@ -95,6 +103,5 @@ std::vector<Function*> EntryTable::FindWithAddress(uint32_t address) {
|
||||
}
|
||||
return fns;
|
||||
}
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/mutex.h"
|
||||
|
||||
#include "xenia/base/split_map.h"
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
@@ -48,7 +48,8 @@ class EntryTable {
|
||||
private:
|
||||
xe::global_critical_region global_critical_region_;
|
||||
// TODO(benvanik): replace with a better data structure.
|
||||
std::unordered_map<uint32_t, Entry*> map_;
|
||||
xe::split_map<uint32_t, Entry*> map_;
|
||||
//std::unordered_map<uint32_t, Entry*> map_;
|
||||
};
|
||||
|
||||
} // namespace cpu
|
||||
|
||||
Reference in New Issue
Block a user