JIT: Reuse trampolines when possible

This commit is contained in:
hthh 2014-10-04 11:04:46 +10:00
parent 16d3604211
commit c7208318fb
4 changed files with 100 additions and 11 deletions

View file

@ -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;
}

View file

@ -20,6 +20,8 @@ struct InstructionInfo
bool byteSwap;
u64 immediate;
s32 displacement;
bool operator==(const InstructionInfo &other) const;
};
struct ModRM

View file

@ -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<int>()(k.registersInUse);
res ^= std::hash<int>()(k.info.operandSize) >> 1;
res ^= std::hash<int>()(k.info.regOperandReg) >> 2;
res ^= std::hash<int>()(k.info.scaledReg) >> 3;
res ^= std::hash<u64>()(k.info.immediate) >> 4;
res ^= std::hash<int>()(k.pc) >> 5;
res ^= std::hash<int>()(k.info.displacement) << 1;
res ^= std::hash<bool>()(k.info.signExtend) << 2;
res ^= std::hash<bool>()(k.info.hasImmediate) << 3;
res ^= std::hash<bool>()(k.info.isMemoryWrite) << 4;
return res;
}
bool TrampolineCacheKey::operator==(const TrampolineCacheKey &other) const
{
return pc == other.pc &&
registersInUse == other.registersInUse &&
info == other.info;
}

View file

@ -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<TrampolineCacheKey, const u8*, TrampolineCacheKeyHasher> cachedTrampolines;
};