Move architecture-specific code out of JitBlockCache

This commit is contained in:
Henrik Rydgard 2016-05-01 10:25:01 +02:00
parent a5be0976bd
commit 5aadce59a2
9 changed files with 98 additions and 96 deletions

View File

@ -271,17 +271,17 @@ const u8 *ArmJit::DoJit(u32 em_address, JitBlock *b)
JumpTarget backJump = GetCodePtr();
gpr.SetRegImm(R0, js.blockStart);
B((const void *)outerLoopPCInR0);
b->checkedEntry = GetCodePtr();
b->checkedEntry = (u8 *)GetCodePtr();
SetCC(CC_LT);
B(backJump);
SetCC(CC_AL);
} else if (jo.useForwardJump) {
b->checkedEntry = GetCodePtr();
b->checkedEntry = (u8 *)GetCodePtr();
SetCC(CC_LT);
bail = B();
SetCC(CC_AL);
} else {
b->checkedEntry = GetCodePtr();
b->checkedEntry = (u8 *)GetCodePtr();
SetCC(CC_LT);
gpr.SetRegImm(R0, js.blockStart);
B((const void *)outerLoopPCInR0);
@ -397,6 +397,37 @@ void ArmJit::Comp_RunBlock(MIPSOpcode op)
ERROR_LOG(JIT, "Comp_RunBlock should never be reached!");
}
void ArmJit::LinkBlock(u8 *exitPoint, const u8 *checkedEntry) {
ARMXEmitter emit(exitPoint);
u32 op = *((const u32 *)emit.GetCodePtr());
bool prelinked = (op & 0xFF000000) == 0xEA000000;
// Jump directly to the block, yay.
emit.B(checkedEntry);
if (!prelinked) {
do {
op = *((const u32 *)emit.GetCodePtr());
// Overwrite whatever is here with a breakpoint.
emit.BKPT(1);
// Stop after overwriting the next unconditional branch or BKPT.
// It can be a BKPT if we unlinked, and are now linking a different one.
} while ((op & 0xFF000000) != 0xEA000000 && (op & 0xFFF000F0) != 0xE1200070);
}
emit.FlushIcache();
}
void ArmJit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) {
// Send anyone who tries to run this block back to the dispatcher.
// Not entirely ideal, but .. pretty good.
// I hope there's enough space...
// checkedEntry is the only "linked" entrance so it's enough to overwrite that.
ARMXEmitter emit(checkedEntry);
emit.MOVI2R(R0, originalAddress);
emit.STR(R0, CTXREG, offsetof(MIPSState, pc));
emit.B(MIPSComp::jit->GetDispatcher());
emit.FlushIcache();
}
bool ArmJit::ReplaceJalTo(u32 dest) {
#ifdef ARM
const ReplacementTableEntry *entry = nullptr;

View File

@ -179,6 +179,9 @@ public:
return dispatcher;
}
void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override;
void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override;
private:
const u8 *DoJit(u32 em_address, JitBlock *b);

View File

@ -262,13 +262,13 @@ const u8 *Arm64Jit::DoJit(u32 em_address, JitBlock *b) {
const u8 *backJump = GetCodePtr();
MOVI2R(SCRATCH1, js.blockStart);
B((const void *)outerLoopPCInSCRATCH1);
b->checkedEntry = GetCodePtr();
b->checkedEntry = (u8 *)GetCodePtr();
B(CC_LT, backJump);
} else if (jo.useForwardJump) {
b->checkedEntry = GetCodePtr();
b->checkedEntry = (u8 *)GetCodePtr();
bail = B(CC_LT);
} else if (jo.enableBlocklink) {
b->checkedEntry = GetCodePtr();
b->checkedEntry = (u8 *)GetCodePtr();
MOVI2R(SCRATCH1, js.blockStart);
FixupBranch skip = B(CC_GE);
B((const void *)outerLoopPCInSCRATCH1);
@ -395,6 +395,24 @@ void Arm64Jit::Comp_RunBlock(MIPSOpcode op) {
ERROR_LOG(JIT, "Comp_RunBlock should never be reached!");
}
void Arm64Jit::LinkBlock(u8 *exitPoint, const u8 *checkedEntry) {
ARM64XEmitter emit(exitPoint);
emit.B(checkedEntry);
// TODO: Write stuff after.
emit.FlushIcache();
}
void Arm64Jit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) {
// Send anyone who tries to run this block back to the dispatcher.
// Not entirely ideal, but .. works.
// Spurious entrances from previously linked blocks can only come through checkedEntry
ARM64XEmitter emit(checkedEntry);
emit.MOVI2R(SCRATCH1, originalAddress);
emit.STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, pc));
emit.B(MIPSComp::jit->GetDispatcher());
emit.FlushIcache();
}
bool Arm64Jit::ReplaceJalTo(u32 dest) {
#ifdef ARM64
const ReplacementTableEntry *entry = nullptr;

View File

@ -180,6 +180,9 @@ public:
return dispatcher;
}
void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override;
void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override;
private:
void GenerateFixedCode(const JitOptions &jo);
void FlushAll();

View File

@ -44,9 +44,6 @@
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "Core/MIPS/JitCommon/NativeJit.h"
#if defined(_M_IX86) || defined(_M_X64)
#include "Common/x64Analyzer.h"
#endif
// #include "JitBase.h"
#if defined USE_OPROFILE && USE_OPROFILE
@ -61,18 +58,6 @@ op_agent_t agent;
#pragma comment(lib, "jitprofiling.lib")
#endif
#ifdef ARM
#include "Core/MIPS/ARM/ArmRegCache.h"
using namespace ArmGen;
using namespace ArmJitConstants;
#elif defined(_M_X64) || defined(_M_IX86)
using namespace Gen;
#elif defined(ARM64)
#include "Core/MIPS/ARM64/Arm64RegCache.h"
using namespace Arm64Gen;
using namespace Arm64JitConstants;
#endif
const u32 INVALID_EXIT = 0xFFFFFFFF;
JitBlockCache::JitBlockCache(MIPSState *mips, NativeCodeBlock *codeBlock) :
@ -198,7 +183,7 @@ void JitBlockCache::ProxyBlock(u32 rootAddress, u32 startAddress, u32 size, cons
// Make binary searches and stuff work ok
b.normalEntry = codePtr;
b.checkedEntry = codePtr;
b.checkedEntry = (u8 *)codePtr; // Ugh, casting away const..
proxyBlockMap_.insert(std::make_pair(startAddress, num_blocks_));
AddBlockMap(num_blocks_);
@ -416,46 +401,8 @@ void JitBlockCache::LinkBlockExits(int i) {
JitBlock &eb = blocks_[destinationBlock];
// Make sure the destination is not invalid.
if (!eb.invalid) {
#if defined(ARM)
ARMXEmitter emit(b.exitPtrs[e]);
u32 op = *((const u32 *)emit.GetCodePtr());
bool prelinked = (op & 0xFF000000) == 0xEA000000;
// Jump directly to the block, yay.
emit.B(eb.checkedEntry);
if (!prelinked) {
do {
op = *((const u32 *)emit.GetCodePtr());
// Overwrite whatever is here with a breakpoint.
emit.BKPT(1);
// Stop after overwriting the next unconditional branch or BKPT.
// It can be a BKPT if we unlinked, and are now linking a different one.
} while ((op & 0xFF000000) != 0xEA000000 && (op & 0xFFF000F0) != 0xE1200070);
}
emit.FlushIcache();
MIPSComp::jit->LinkBlock(b.exitPtrs[e], eb.checkedEntry);
b.linkStatus[e] = true;
#elif defined(_M_IX86) || defined(_M_X64)
XEmitter emit(b.exitPtrs[e]);
// Okay, this is a bit ugly, but we check here if it already has a JMP.
// That means it doesn't have a full exit to pad with INT 3.
bool prelinked = *emit.GetCodePtr() == 0xE9;
emit.JMP(eb.checkedEntry, true);
if (!prelinked) {
ptrdiff_t actualSize = emit.GetWritableCodePtr() - b.exitPtrs[e];
int pad = JitBlockCache::GetBlockExitSize() - (int)actualSize;
for (int i = 0; i < pad; ++i) {
emit.INT3();
}
}
b.linkStatus[e] = true;
#elif defined(ARM64)
ARM64XEmitter emit(b.exitPtrs[e]);
emit.B(eb.checkedEntry);
// TODO: Write stuff after.
emit.FlushIcache();
b.linkStatus[e] = true;
#endif
}
}
}
@ -588,39 +535,7 @@ void JitBlockCache::DestroyBlock(int block_num, bool invalidate) {
return;
}
#if defined(ARM)
// Send anyone who tries to run this block back to the dispatcher.
// Not entirely ideal, but .. pretty good.
// I hope there's enough space...
// checkedEntry is the only "linked" entrance so it's enough to overwrite that.
ARMXEmitter emit((u8 *)b->checkedEntry);
emit.MOVI2R(R0, b->originalAddress);
emit.STR(R0, CTXREG, offsetof(MIPSState, pc));
emit.B(MIPSComp::jit->GetDispatcher());
emit.FlushIcache();
#elif defined(_M_IX86) || defined(_M_X64)
// Send anyone who tries to run this block back to the dispatcher.
// Not entirely ideal, but .. pretty good.
// Spurious entrances from previously linked blocks can only come through checkedEntry
XEmitter emit((u8 *)b->checkedEntry);
emit.MOV(32, M(&mips_->pc), Imm32(b->originalAddress));
emit.JMP(MIPSComp::jit->GetDispatcher(), true);
#elif defined(ARM64)
// Send anyone who tries to run this block back to the dispatcher.
// Not entirely ideal, but .. works.
// Spurious entrances from previously linked blocks can only come through checkedEntry
ARM64XEmitter emit((u8 *)b->checkedEntry);
emit.MOVI2R(SCRATCH1, b->originalAddress);
emit.STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, pc));
emit.B(MIPSComp::jit->GetDispatcher());
emit.FlushIcache();
#endif
MIPSComp::jit->UnlinkBlock(b->checkedEntry, b->originalAddress);
}
void JitBlockCache::InvalidateICache(u32 address, const u32 length) {

View File

@ -73,7 +73,7 @@ struct BlockCacheStats {
struct JitBlock {
bool ContainsAddress(u32 em_address);
const u8 *checkedEntry;
u8 *checkedEntry; // not const, may need to write through this to unlink
const u8 *normalEntry;
u8 *exitPtrs[MAX_JIT_BLOCK_EXITS]; // to be able to rewrite the exit jump

View File

@ -49,6 +49,11 @@ namespace MIPSComp {
virtual void ClearCache() = 0;
virtual void EatPrefix() = 0;
// Block linking. This may need to work differently for whole-function JITs and stuff
// like that.
virtual void LinkBlock(u8 *exitPoint, const u8 *entryPoint) = 0;
virtual void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) = 0;
virtual void Comp_Generic(MIPSOpcode op) = 0;
virtual void Comp_RunBlock(MIPSOpcode op) = 0;
virtual void Comp_ReplacementFunc(MIPSOpcode op) = 0;

View File

@ -359,7 +359,7 @@ const u8 *Jit::DoJit(u32 em_address, JitBlock *b)
js.PrefixStart();
// We add a check before the block, used when entering from a linked block.
b->checkedEntry = GetCodePtr();
b->checkedEntry = (u8 *)GetCodePtr();
// Downcount flag check. The last block decremented downcounter, and the flag should still be available.
FixupBranch skip = J_CC(CC_NS);
MOV(32, M(&mips_->pc), Imm32(js.blockStart));
@ -501,6 +501,30 @@ void Jit::Comp_RunBlock(MIPSOpcode op)
ERROR_LOG(JIT, "Comp_RunBlock");
}
void Jit::LinkBlock(u8 *exitPoint, const u8 *checkedEntry) {
XEmitter emit(exitPoint);
// Okay, this is a bit ugly, but we check here if it already has a JMP.
// That means it doesn't have a full exit to pad with INT 3.
bool prelinked = *emit.GetCodePtr() == 0xE9;
emit.JMP(checkedEntry, true);
if (!prelinked) {
ptrdiff_t actualSize = emit.GetWritableCodePtr() - exitPoint;
int pad = JitBlockCache::GetBlockExitSize() - (int)actualSize;
for (int i = 0; i < pad; ++i) {
emit.INT3();
}
}
}
void Jit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) {
// Send anyone who tries to run this block back to the dispatcher.
// Not entirely ideal, but .. pretty good.
// Spurious entrances from previously linked blocks can only come through checkedEntry
XEmitter emit(checkedEntry);
emit.MOV(32, M(&mips_->pc), Imm32(originalAddress));
emit.JMP(MIPSComp::jit->GetDispatcher(), true);
}
bool Jit::ReplaceJalTo(u32 dest) {
const ReplacementTableEntry *entry = nullptr;
u32 funcSize = 0;

View File

@ -174,6 +174,9 @@ public:
return dispatcher;
}
void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override;
void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override;
private:
void GenerateFixedCode(JitOptions &jo);
void GetStateAndFlushAll(RegCacheState &state);