diff --git a/Source/Core/Common/x64Analyzer.cpp b/Source/Core/Common/x64Analyzer.cpp index 5be91ea585..7e8b40e6cd 100644 --- a/Source/Core/Common/x64Analyzer.cpp +++ b/Source/Core/Common/x64Analyzer.cpp @@ -223,3 +223,19 @@ bool DisassembleMov(const unsigned char *codePtr, InstructionInfo *info) info->instructionSize = (int)(codePtr - startCodePtr); return true; } + +bool InstructionInfo::operator==(const InstructionInfo &other) const +{ + return operandSize == other.operandSize && + instructionSize == other.instructionSize && + regOperandReg == other.regOperandReg && + otherReg == other.otherReg && + scaledReg == other.scaledReg && + zeroExtend == other.zeroExtend && + signExtend == other.signExtend && + hasImmediate == other.hasImmediate && + isMemoryWrite == other.isMemoryWrite && + byteSwap == other.byteSwap && + immediate == other.immediate && + displacement == other.displacement; +} diff --git a/Source/Core/Common/x64Analyzer.h b/Source/Core/Common/x64Analyzer.h index 4858f099da..c87a89a51f 100644 --- a/Source/Core/Common/x64Analyzer.h +++ b/Source/Core/Common/x64Analyzer.h @@ -20,6 +20,8 @@ struct InstructionInfo bool byteSwap; u64 immediate; s32 displacement; + + bool operator==(const InstructionInfo &other) const; }; struct ModRM diff --git a/Source/Core/Core/PowerPC/JitCommon/TrampolineCache.cpp b/Source/Core/Core/PowerPC/JitCommon/TrampolineCache.cpp index 5e961bc6e5..d1ebfc5789 100644 --- a/Source/Core/Core/PowerPC/JitCommon/TrampolineCache.cpp +++ b/Source/Core/Core/PowerPC/JitCommon/TrampolineCache.cpp @@ -19,25 +19,42 @@ using namespace Gen; -extern u8 *trampolineCodePtr; - void TrampolineCache::Init() { - AllocCodeSpace(4 * 1024 * 1024); + AllocCodeSpace(8 * 1024 * 1024); +} + +void TrampolineCache::ClearCodeSpace() +{ + X64CodeBlock::ClearCodeSpace(); + cachedTrampolines.clear(); } void TrampolineCache::Shutdown() { FreeCodeSpace(); + cachedTrampolines.clear(); } -// Extremely simplistic - just generate the requested trampoline. May reuse them in the future. -const u8 *TrampolineCache::GetReadTrampoline(const InstructionInfo &info, u32 registersInUse) +const u8* TrampolineCache::GetReadTrampoline(const InstructionInfo &info, u32 registersInUse) +{ + TrampolineCacheKey key = { registersInUse, 0, info }; + + auto it = cachedTrampolines.find(key); + if (it != cachedTrampolines.end()) + return it->second; + + const u8* trampoline = GenerateReadTrampoline(info, registersInUse); + cachedTrampolines[key] = trampoline; + return trampoline; +} + +const u8* TrampolineCache::GenerateReadTrampoline(const InstructionInfo &info, u32 registersInUse) { if (GetSpaceLeft() < 1024) PanicAlert("Trampoline cache full"); - const u8 *trampoline = GetCodePtr(); + const u8* trampoline = GetCodePtr(); X64Reg addrReg = (X64Reg)info.scaledReg; X64Reg dataReg = (X64Reg)info.regOperandReg; @@ -80,13 +97,25 @@ const u8 *TrampolineCache::GetReadTrampoline(const InstructionInfo &info, u32 re return trampoline; } -// Extremely simplistic - just generate the requested trampoline. May reuse them in the future. -const u8 *TrampolineCache::GetWriteTrampoline(const InstructionInfo &info, u32 registersInUse, u32 pc) +const u8* TrampolineCache::GetWriteTrampoline(const InstructionInfo &info, u32 registersInUse, u32 pc) +{ + TrampolineCacheKey key = { registersInUse, pc, info }; + + auto it = cachedTrampolines.find(key); + if (it != cachedTrampolines.end()) + return it->second; + + const u8* trampoline = GenerateWriteTrampoline(info, registersInUse, pc); + cachedTrampolines[key] = trampoline; + return trampoline; +} + +const u8* TrampolineCache::GenerateWriteTrampoline(const InstructionInfo &info, u32 registersInUse, u32 pc) { if (GetSpaceLeft() < 1024) PanicAlert("Trampoline cache full"); - const u8 *trampoline = GetCodePtr(); + const u8* trampoline = GetCodePtr(); X64Reg dataReg = (X64Reg)info.regOperandReg; X64Reg addrReg = (X64Reg)info.scaledReg; @@ -153,4 +182,25 @@ const u8 *TrampolineCache::GetWriteTrampoline(const InstructionInfo &info, u32 r return trampoline; } +size_t TrampolineCacheKeyHasher::operator()(const TrampolineCacheKey& k) const +{ + size_t res = std::hash()(k.registersInUse); + res ^= std::hash()(k.info.operandSize) >> 1; + res ^= std::hash()(k.info.regOperandReg) >> 2; + res ^= std::hash()(k.info.scaledReg) >> 3; + res ^= std::hash()(k.info.immediate) >> 4; + res ^= std::hash()(k.pc) >> 5; + res ^= std::hash()(k.info.displacement) << 1; + res ^= std::hash()(k.info.signExtend) << 2; + res ^= std::hash()(k.info.hasImmediate) << 3; + res ^= std::hash()(k.info.isMemoryWrite) << 4; + return res; +} + +bool TrampolineCacheKey::operator==(const TrampolineCacheKey &other) const +{ + return pc == other.pc && + registersInUse == other.registersInUse && + info == other.info; +} diff --git a/Source/Core/Core/PowerPC/JitCommon/TrampolineCache.h b/Source/Core/Core/PowerPC/JitCommon/TrampolineCache.h index 516a071ac2..bf7109d6d9 100644 --- a/Source/Core/Core/PowerPC/JitCommon/TrampolineCache.h +++ b/Source/Core/Core/PowerPC/JitCommon/TrampolineCache.h @@ -11,12 +11,33 @@ // We need at least this many bytes for backpatching. const int BACKPATCH_SIZE = 5; +struct TrampolineCacheKey +{ + u32 registersInUse; + u32 pc; + InstructionInfo info; + + bool operator==(const TrampolineCacheKey &other) const; +}; + +struct TrampolineCacheKeyHasher +{ + size_t operator()(const TrampolineCacheKey& k) const; +}; + class TrampolineCache : public Gen::X64CodeBlock { public: void Init(); void Shutdown(); - const u8 *GetReadTrampoline(const InstructionInfo &info, u32 registersInUse); - const u8 *GetWriteTrampoline(const InstructionInfo &info, u32 registersInUse, u32 pc); + const u8* GetReadTrampoline(const InstructionInfo &info, u32 registersInUse); + const u8* GetWriteTrampoline(const InstructionInfo &info, u32 registersInUse, u32 pc); + void ClearCodeSpace(); + +private: + const u8* GenerateReadTrampoline(const InstructionInfo &info, u32 registersInUse); + const u8* GenerateWriteTrampoline(const InstructionInfo &info, u32 registersInUse, u32 pc); + + std::unordered_map cachedTrampolines; };