Fix some replace-related bugs. Add "jal" replace inlining, not activated.

This commit is contained in:
Henrik Rydgard 2013-12-18 16:27:23 +01:00
parent b2d0f37d96
commit 1e300447e1
8 changed files with 90 additions and 26 deletions

View file

@ -52,6 +52,7 @@ static int Replace_atan2f() {
RETURNF(atan2f(f1, f2));
return 120; // guess number of cycles
}
// Should probably do JIT versions of this, possibly ones that only delegate
// large copies to a C function.
static int Replace_memcpy() {
@ -151,12 +152,11 @@ static const ReplacementTableEntry entries[] = {
{ "memcpy", &Replace_memcpy, 0, 0},
{ "memmove", &Replace_memmove, 0, 0},
{ "memset", &Replace_memset, 0, 0},
{ "strlen", &Replace_strlen, 0, 0},
{ "strcpy", &Replace_strcpy, 0, 0},
{ "fabsf", 0, &MIPSComp::Jit::Replace_fabsf, REPFLAG_ALLOWINLINE},
{ "strcmp", &Replace_strcmp, 0, 0},
{ "strncmp", &Replace_strncmp, 0, 0},
{ "fabsf", 0, &MIPSComp::Jit::Replace_fabsf, REPFLAG_ALLOWINLINE},
// { "vmmul_q_transp", &Replace_vmmul_q_transp, 0, 0},
{}
};
@ -208,11 +208,16 @@ void WriteReplaceInstruction(u32 address, u64 hash, int size) {
}
bool GetReplacedOpAt(u32 address, u32 *op) {
auto iter = replacedInstructions.find(address);
if (iter != replacedInstructions.end()) {
*op = iter->second;
return true;
} else {
return false;
u32 instr = Memory::Read_U32(address);
if (MIPS_IS_REPLACEMENT(instr)) {
auto iter = replacedInstructions.find(address);
if (iter != replacedInstructions.end()) {
*op = iter->second;
return true;
} else {
return false;
}
}
*op = instr;
return true;
}

View file

@ -334,6 +334,7 @@ namespace MIPSDis
const char *name = MIPSGetName(op);
sprintf(out, "%s\t->$%08x",name,addr);
}
void Dis_JumpRegType(MIPSOpcode op, char *out)
{
int rs = _RS;
@ -363,11 +364,18 @@ namespace MIPSDis
void Dis_Emuhack(MIPSOpcode op, char *out)
{
//const char *name = MIPSGetName(op);
//sprintf(out,"%s\t-",name);
out[0]='*';
out[1]=0;
// MIPSDisAsm(MIPSComp::GetOriginalOp(op), currentDebugMIPS->GetPC(), out+1);
switch (op.encoding >> 24) {
case 0x68:
strcpy(out, "* jitblock");
break;
case 0x6a:
strcpy(out, "* replacement");
break;
default:
out[0]='*';
out[1]=0;
break;
}
}

View file

@ -552,6 +552,10 @@ void Jit::Comp_Jump(MIPSOpcode op)
break;
case 3: //jal
// Special case for branches to "replace functions":
// if (ReplaceJalTo(targetAddr))
// return;
// Save return address - might be overwritten by delay slot.
gpr.SetImm(MIPS_REG_RA, js.compilerPC + 8);
CompileDelaySlot(DELAYSLOT_NICE);

View file

@ -27,6 +27,7 @@ int Jit::Replace_fabsf() {
fpr.MapReg(0, MAP_DIRTY | MAP_NOINIT);
MOVSS(fpr.RX(0), fpr.R(12));
ANDPS(fpr.RX(0), M((void *)&ssNoSignMask));
fpr.ReleaseSpillLocks();
return 4; // Number of instructions in the MIPS function
}

View file

@ -229,7 +229,7 @@ void Jit::CompileDelaySlot(int flags, RegCacheState *state)
SAVE_FLAGS; // preserve flag around the delay slot!
js.inDelaySlot = true;
MIPSOpcode op = Memory::Read_Instruction(addr);
MIPSOpcode op = Memory::Read_Opcode_JIT(addr);
MIPSCompileOp(op);
js.inDelaySlot = false;
@ -323,7 +323,7 @@ const u8 *Jit::DoJit(u32 em_address, JitBlock *b)
// Jit breakpoints are quite fast, so let's do them in release too.
CheckJitBreakpoint(js.compilerPC, 0);
MIPSOpcode inst = Memory::Read_Instruction(js.compilerPC);
MIPSOpcode inst = Memory::Read_Opcode_JIT(js.compilerPC);
js.downcountAmount += MIPSGetInstructionCycleEstimate(inst);
MIPSCompileOp(inst);
@ -372,6 +372,35 @@ void Jit::Comp_RunBlock(MIPSOpcode op)
ERROR_LOG(JIT, "Comp_RunBlock");
}
bool Jit::ReplaceJalTo(u32 dest) {
MIPSOpcode op(Memory::Read_U32(dest));
if (!MIPS_IS_REPLACEMENT(dest))
return false;
int index = op.encoding & MIPS_EMUHACK_VALUE_MASK;
const ReplacementTableEntry *entry = GetReplacementFunc(index);
if (!entry) {
ERROR_LOG(HLE, "ReplaceJalTo: Invalid replacement op %08x at %08x", op.encoding, dest);
return false;
}
// Warning - this might be bad if the code at the destination changes...
if (entry->flags & REPFLAG_ALLOWINLINE) {
// Jackpot! Just do it, no flushing. The code will be entirely inlined.
// First, compile the delay slot. It's unconditional so no issues.
CompileDelaySlot(DELAYSLOT_NICE);
// Technically, we should write the unused return address to RA, but meh.
MIPSReplaceFunc repl = entry->jitReplaceFunc;
int cycles = (this->*repl)();
js.downcountAmount += cycles;
// No writing exits, keep going!
return true;
} else {
return false;
}
}
void Jit::Comp_ReplacementFunc(MIPSOpcode op)
{
// We get here if we execute the first instruction of a replaced function. This means

View file

@ -167,12 +167,14 @@ public:
void ClearCache();
void ClearCacheAt(u32 em_address, int length = 4);
private:
void GetStateAndFlushAll(RegCacheState &state);
void RestoreState(const RegCacheState state);
void FlushAll();
void FlushPrefixV();
void WriteDowncount(int offset = 0);
bool ReplaceJalTo(u32 dest);
// See CompileDelaySlotFlags for flags.
void CompileDelaySlot(int flags, RegCacheState *state = NULL);

View file

@ -171,15 +171,22 @@ void Clear()
memset(m_pVRAM, 0, VRAM_SIZE);
}
Opcode Read_Instruction(u32 address)
{
Opcode inst = Opcode(Read_U32(address));
if (MIPS_IS_RUNBLOCK(inst.encoding) && MIPSComp::jit)
{
if (MIPS_IS_RUNBLOCK(inst.encoding) && MIPSComp::jit) {
JitBlockCache *bc = MIPSComp::jit->GetBlockCache();
int block_num = bc->GetBlockNumberFromEmuHackOp(inst, true);
if (block_num >= 0) {
return bc->GetOriginalFirstOp(block_num);
inst = bc->GetOriginalFirstOp(block_num);
/*
u32 op;
if (GetReplacedOpAt(address, &op)) {
return Opcode(op);
}*/
return inst;
} else {
return inst;
}
@ -197,7 +204,18 @@ Opcode Read_Instruction(u32 address)
Opcode Read_Opcode_JIT(u32 address)
{
return Read_Instruction(address);
Opcode inst = Opcode(Read_U32(address));
if (MIPS_IS_RUNBLOCK(inst.encoding) && MIPSComp::jit) {
JitBlockCache *bc = MIPSComp::jit->GetBlockCache();
int block_num = bc->GetBlockNumberFromEmuHackOp(inst, true);
if (block_num >= 0) {
return bc->GetOriginalFirstOp(block_num);
} else {
return inst;
}
} else {
return inst;
}
}
// WARNING! No checks!

View file

@ -133,17 +133,14 @@ struct Opcode {
u32 encoding;
};
// used by JIT to read instructions
// used by JIT to read instructions. Does not resolve replacements.
Opcode Read_Opcode_JIT(const u32 _Address);
// used by JIT. uses iCacheJIT. Reads in the "Locked cache" mode
// used by JIT. Reads in the "Locked cache" mode
void Write_Opcode_JIT(const u32 _Address, const Opcode _Value);
// this is used by Debugger a lot.
// For now, just reads from memory!
// Should be used by analyzers, disassemblers etc. Does resolve replacements.
Opcode Read_Instruction(const u32 _Address);
// For use by emulator
u8 Read_U8(const u32 _Address);
u16 Read_U16(const u32 _Address);
u32 Read_U32(const u32 _Address);