LLE JIT: Reworked the block linking code. It now keeps track of what each block is waiting on, minimising the amount of recompiling. Both jumps and calls can now become linked. The code also checks the cycle count before jumping to the linked block.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6728 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
skidau 2011-01-03 10:39:48 +00:00
parent fea85945da
commit a6637c257f
4 changed files with 78 additions and 128 deletions

View File

@ -32,8 +32,7 @@ enum
CODE_IDLE_SKIP = 2, CODE_IDLE_SKIP = 2,
CODE_LOOP_START = 4, CODE_LOOP_START = 4,
CODE_LOOP_END = 8, CODE_LOOP_END = 8,
CODE_CALL = 16, CODE_UPDATE_SR = 16,
CODE_UPDATE_SR = 32,
}; };
// Easy to query array covering the whole of instruction memory. // Easy to query array covering the whole of instruction memory.

View File

@ -31,9 +31,6 @@
using namespace Gen; using namespace Gen;
const u8 *stubEntryPoint; const u8 *stubEntryPoint;
u16 blocksCompiled;
u16 unresolvedCalls;
u16 unresolvedCallsThisBlock;
DSPEmitter::DSPEmitter() : storeIndex(-1), storeIndex2(-1) DSPEmitter::DSPEmitter() : storeIndex(-1), storeIndex2(-1)
{ {
@ -59,8 +56,6 @@ DSPEmitter::DSPEmitter() : storeIndex(-1), storeIndex2(-1)
blockLinks[i] = 0; blockLinks[i] = 0;
blockSize[i] = 0; blockSize[i] = 0;
} }
blocksCompiled = 0;
unresolvedCalls = 0;
} }
DSPEmitter::~DSPEmitter() DSPEmitter::~DSPEmitter()
@ -79,8 +74,6 @@ void DSPEmitter::ClearIRAM() {
blockLinks[i] = 0; blockLinks[i] = 0;
blockSize[i] = 0; blockSize[i] = 0;
} }
blocksCompiled = 0;
unresolvedCalls = 0;
} }
// Must go out of block if exception is detected // Must go out of block if exception is detected
@ -196,41 +189,11 @@ void DSPEmitter::unknown_instruction(UDSPInstruction inst)
PanicAlert("unknown_instruction %04x - Fix me ;)", inst); PanicAlert("unknown_instruction %04x - Fix me ;)", inst);
} }
void DSPEmitter::ClearCallFlag() void DSPEmitter::Compile(u16 start_addr)
{
--unresolvedCallsThisBlock;
}
void DSPEmitter::Compile(int start_addr)
{ {
// Remember the current block address for later // Remember the current block address for later
startAddr = start_addr; startAddr = start_addr;
blocksCompiled++; unresolvedJumps[start_addr].clear();
unresolvedCallsThisBlock = 0;
// If the number of unresolved calls exceeds 8, there is a critical
// block that probably cannot be resolved. If this occurs, quit linking
// blocks. Currently occurs in the zelda ucode.
if (unresolvedCalls <= 8)
{
// After every 10 blocks, clear out the blocks that have unresolved
// calls, and reattempt relinking.
if (blocksCompiled >= 10 && unresolvedCalls > 0)
{
for(int i = 0x0000; i < MAX_BLOCKS; ++i)
{
if (DSPAnalyzer::code_flags[i] & DSPAnalyzer::CODE_CALL)
{
blocks[i] = (CompiledCode)stubEntryPoint;
blockLinks[i] = 0;
blockSize[i] = 0;
}
}
// Reset and reattempt relinking
blocksCompiled = 0;
unresolvedCalls = 0;
}
}
const u8 *entryPoint = AlignCode16(); const u8 *entryPoint = AlignCode16();
ABI_PushAllCalleeSavedRegsAndAdjustStack(); ABI_PushAllCalleeSavedRegsAndAdjustStack();
@ -288,19 +251,14 @@ void DSPEmitter::Compile(int start_addr)
UDSPInstruction inst = dsp_imem_read(compilePC); UDSPInstruction inst = dsp_imem_read(compilePC);
const DSPOPCTemplate *opcode = GetOpTemplate(inst); const DSPOPCTemplate *opcode = GetOpTemplate(inst);
// Scan for CALL's to delay block link. TODO: Scan for J_CC after it is jitted.
if (opcode->jitFunc &&
((opcode->opcode >= 0x0290 && opcode->opcode <= 0x029f) ||
(opcode->opcode >= 0x02b0 && opcode->opcode <= 0x02bf)))
{
++unresolvedCallsThisBlock;
}
EmitInstruction(inst); EmitInstruction(inst);
blockSize[start_addr]++; blockSize[start_addr]++;
compilePC += opcode->size; compilePC += opcode->size;
// If the block was trying to link into itself, remove the link
unresolvedJumps[start_addr].remove(compilePC);
fixup_pc = true; fixup_pc = true;
// Handle loop condition, only if current instruction was flagged as a loop destination // Handle loop condition, only if current instruction was flagged as a loop destination
@ -411,16 +369,26 @@ void DSPEmitter::Compile(int start_addr)
// Mark this block as a linkable destination if it does not contain // Mark this block as a linkable destination if it does not contain
// any unresolved CALL's // any unresolved CALL's
if (unresolvedCallsThisBlock == 0) if (unresolvedJumps[start_addr].empty())
{ {
DSPAnalyzer::code_flags[start_addr] &= ~DSPAnalyzer::CODE_CALL;
blockLinks[start_addr] = (CompiledCode)blockLinkEntry; blockLinks[start_addr] = (CompiledCode)blockLinkEntry;
}
else for(u16 i = 0x0000; i < 0xffff; ++i)
{ {
DSPAnalyzer::code_flags[start_addr] |= DSPAnalyzer::CODE_CALL; if (!unresolvedJumps[i].empty())
blockLinks[start_addr] = 0; {
++unresolvedCalls; // Check if there were any blocks waiting for this block to be linkable
int size = unresolvedJumps[i].size();
unresolvedJumps[i].remove(start_addr);
if (unresolvedJumps[i].size() < size)
{
// Mark the block to be recompiled again
blocks[i] = (CompiledCode)stubEntryPoint;
blockLinks[i] = 0;
blockSize[i] = 0;
}
}
}
} }
if (blockSize[start_addr] == 0) if (blockSize[start_addr] == 0)

View File

@ -18,6 +18,8 @@
#ifndef _DSPEMITTER_H #ifndef _DSPEMITTER_H
#define _DSPEMITTER_H #define _DSPEMITTER_H
#include <list>
#include "DSPCommon.h" #include "DSPCommon.h"
#include "x64Emitter.h" #include "x64Emitter.h"
@ -41,7 +43,7 @@ public:
void CompileDispatcher(); void CompileDispatcher();
const u8 *CompileStub(); const u8 *CompileStub();
void Compile(int start_addr); void Compile(u16 start_addr);
void ClearCallFlag(); void ClearCallFlag();
void Default(UDSPInstruction inst); void Default(UDSPInstruction inst);
@ -252,6 +254,7 @@ public:
u16 startAddr; u16 startAddr;
CompiledCode *blockLinks; CompiledCode *blockLinks;
u16 *blockSize; u16 *blockSize;
std::list<u16> unresolvedJumps[0x10000];
private: private:
CompiledCode *blocks; CompiledCode *blocks;

View File

@ -162,15 +162,14 @@ void WriteBranchExit(DSPEmitter& emitter)
emitter.RET(); emitter.RET();
} }
void r_jcc(const UDSPInstruction opc, DSPEmitter& emitter) void WriteBlockLink(DSPEmitter& emitter, u16 dest)
{ {
u16 dest = dsp_imem_read(emitter.compilePC + 1);
#ifdef _M_IX86 // All32
emitter.MOV(16, M(&(g_dsp.pc)), Imm16(dest));
// Jump directly to the called block if it has already been compiled. // Jump directly to the called block if it has already been compiled.
if (emitter.blockLinks[dest]) if (!(dest >= emitter.startAddr && dest <= emitter.compilePC))
{ {
if (emitter.blockLinks[dest] != 0 )
{
#ifdef _M_IX86 // All32
// Check if we have enough cycles to execute the next block // Check if we have enough cycles to execute the next block
emitter.MOV(16, R(ESI), M(&cyclesLeft)); emitter.MOV(16, R(ESI), M(&cyclesLeft));
emitter.CMP(16, R(ESI), Imm16(emitter.blockSize[emitter.startAddr] + emitter.blockSize[dest])); emitter.CMP(16, R(ESI), Imm16(emitter.blockSize[emitter.startAddr] + emitter.blockSize[dest]));
@ -179,32 +178,42 @@ void r_jcc(const UDSPInstruction opc, DSPEmitter& emitter)
emitter.SUB(16, R(ESI), Imm16(emitter.blockSize[emitter.startAddr])); emitter.SUB(16, R(ESI), Imm16(emitter.blockSize[emitter.startAddr]));
emitter.MOV(16, M(&cyclesLeft), R(ESI)); emitter.MOV(16, M(&cyclesLeft), R(ESI));
emitter.JMPptr(M(&emitter.blockLinks[dest])); emitter.JMPptr(M(&emitter.blockLinks[dest]));
emitter.ClearCallFlag();
emitter.SetJumpTarget(notEnoughCycles); emitter.SetJumpTarget(notEnoughCycles);
#else
// Check if we have enough cycles to execute the next block
emitter.CMP(16, R(R12), Imm16(emitter.blockSize[emitter.startAddr] + emitter.blockSize[dest]));
FixupBranch notEnoughCycles = emitter.J_CC(CC_BE);
emitter.SUB(16, R(R12), Imm16(emitter.blockSize[emitter.startAddr]));
emitter.MOV(64, R(RAX), ImmPtr((void *)emitter.blockLinks[dest]));
emitter.JMPptr(R(RAX));
emitter.SetJumpTarget(notEnoughCycles);
#endif
} }
else
{
// The destination has not been compiled yet. Add it to the list
// of blocks that this block is waiting on.
emitter.unresolvedJumps[emitter.startAddr].push_back(dest);
}
}
}
void r_jcc(const UDSPInstruction opc, DSPEmitter& emitter)
{
u16 dest = dsp_imem_read(emitter.compilePC + 1);
const DSPOPCTemplate *opcode = GetOpTemplate(opc);
// If the block is unconditional, attempt to link block
if (opcode->uncond_branch)
WriteBlockLink(emitter, dest);
#ifdef _M_IX86 // All32
emitter.MOV(16, M(&(g_dsp.pc)), Imm16(dest));
#else #else
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc))); emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
emitter.MOV(16, MatR(RAX), Imm16(dest)); emitter.MOV(16, MatR(RAX), Imm16(dest));
// Jump directly to the next block if it has already been compiled.
if (emitter.blockLinks[dest])
{
// Check if we have enough cycles to execute the next block
//emitter.MOV(64, R(R12), ImmPtr(&cyclesLeft));
//emitter.MOV(16, R(RAX), MatR(R12));
//emitter.CMP(16, R(RAX), Imm16(emitter.blockSize[emitter.startAddr] + emitter.blockSize[dest]));
//FixupBranch notEnoughCycles = emitter.J_CC(CC_BE);
//emitter.SUB(16, R(RAX), Imm16(emitter.blockSize[emitter.startAddr]));
//emitter.MOV(16, MatR(R12), R(RAX));
//emitter.MOV(64, R(RAX), ImmPtr((void *)(emitter.blockLinks[dest])));
//emitter.JMPptr(R(RAX));
//emitter.ClearCallFlag();
//emitter.SetJumpTarget(notEnoughCycles);
}
#endif #endif
WriteBranchExit(emitter); WriteBranchExit(emitter);
} }
@ -261,48 +270,19 @@ void DSPEmitter::jmprcc(const UDSPInstruction opc)
void r_call(const UDSPInstruction opc, DSPEmitter& emitter) void r_call(const UDSPInstruction opc, DSPEmitter& emitter)
{ {
u16 dest = dsp_imem_read(emitter.compilePC + 1);
emitter.MOV(16, R(DX), Imm16(emitter.compilePC + 2)); emitter.MOV(16, R(DX), Imm16(emitter.compilePC + 2));
emitter.dsp_reg_store_stack(DSP_STACK_C); emitter.dsp_reg_store_stack(DSP_STACK_C);
u16 dest = dsp_imem_read(emitter.compilePC + 1);
const DSPOPCTemplate *opcode = GetOpTemplate(opc);
// If the block is unconditional, attempt to link block
if (opcode->uncond_branch)
WriteBlockLink(emitter, dest);
#ifdef _M_IX86 // All32 #ifdef _M_IX86 // All32
emitter.MOV(16, M(&(g_dsp.pc)), Imm16(dest)); emitter.MOV(16, M(&(g_dsp.pc)), Imm16(dest));
// Jump directly to the called block if it has already been compiled.
if (emitter.blockLinks[dest])
{
// Check if we have enough cycles to execute the next block
//emitter.MOV(16, R(ESI), M(&cyclesLeft));
//emitter.CMP(16, R(ESI), Imm16(emitter.blockSize[emitter.startAddr] + emitter.blockSize[dest]));
//FixupBranch notEnoughCycles = emitter.J_CC(CC_BE);
//emitter.SUB(16, R(ESI), Imm16(emitter.blockSize[emitter.startAddr]));
//emitter.MOV(16, M(&cyclesLeft), R(ESI));
//emitter.JMPptr(M(&emitter.blockLinks[dest]));
//emitter.ClearCallFlag();
//emitter.SetJumpTarget(notEnoughCycles);
}
#else #else
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc))); emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
emitter.MOV(16, MatR(RAX), Imm16(dest)); emitter.MOV(16, MatR(RAX), Imm16(dest));
// Jump directly to the called block if it has already been compiled.
if (emitter.blockLinks[dest])
{
// Check if we have enough cycles to execute the next block
emitter.MOV(64, R(R12), ImmPtr(&cyclesLeft));
emitter.MOV(16, R(RAX), MatR(R12));
emitter.CMP(16, R(RAX), Imm16(emitter.blockSize[emitter.startAddr] + emitter.blockSize[dest]));
FixupBranch notEnoughCycles = emitter.J_CC(CC_BE);
emitter.SUB(16, R(RAX), Imm16(emitter.blockSize[emitter.startAddr]));
emitter.MOV(16, MatR(R12), R(RAX));
emitter.MOV(64, R(RAX), ImmPtr((void *)(emitter.blockLinks[dest])));
emitter.JMPptr(R(RAX));
emitter.ClearCallFlag();
emitter.SetJumpTarget(notEnoughCycles);
}
#endif #endif
WriteBranchExit(emitter); WriteBranchExit(emitter);
} }