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:
parent
fea85945da
commit
a6637c257f
|
@ -32,8 +32,7 @@ enum
|
|||
CODE_IDLE_SKIP = 2,
|
||||
CODE_LOOP_START = 4,
|
||||
CODE_LOOP_END = 8,
|
||||
CODE_CALL = 16,
|
||||
CODE_UPDATE_SR = 32,
|
||||
CODE_UPDATE_SR = 16,
|
||||
};
|
||||
|
||||
// Easy to query array covering the whole of instruction memory.
|
||||
|
|
|
@ -31,9 +31,6 @@
|
|||
using namespace Gen;
|
||||
|
||||
const u8 *stubEntryPoint;
|
||||
u16 blocksCompiled;
|
||||
u16 unresolvedCalls;
|
||||
u16 unresolvedCallsThisBlock;
|
||||
|
||||
DSPEmitter::DSPEmitter() : storeIndex(-1), storeIndex2(-1)
|
||||
{
|
||||
|
@ -59,8 +56,6 @@ DSPEmitter::DSPEmitter() : storeIndex(-1), storeIndex2(-1)
|
|||
blockLinks[i] = 0;
|
||||
blockSize[i] = 0;
|
||||
}
|
||||
blocksCompiled = 0;
|
||||
unresolvedCalls = 0;
|
||||
}
|
||||
|
||||
DSPEmitter::~DSPEmitter()
|
||||
|
@ -79,8 +74,6 @@ void DSPEmitter::ClearIRAM() {
|
|||
blockLinks[i] = 0;
|
||||
blockSize[i] = 0;
|
||||
}
|
||||
blocksCompiled = 0;
|
||||
unresolvedCalls = 0;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
void DSPEmitter::ClearCallFlag()
|
||||
{
|
||||
--unresolvedCallsThisBlock;
|
||||
}
|
||||
|
||||
void DSPEmitter::Compile(int start_addr)
|
||||
void DSPEmitter::Compile(u16 start_addr)
|
||||
{
|
||||
// Remember the current block address for later
|
||||
startAddr = start_addr;
|
||||
blocksCompiled++;
|
||||
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;
|
||||
}
|
||||
}
|
||||
unresolvedJumps[start_addr].clear();
|
||||
|
||||
const u8 *entryPoint = AlignCode16();
|
||||
ABI_PushAllCalleeSavedRegsAndAdjustStack();
|
||||
|
@ -288,19 +251,14 @@ void DSPEmitter::Compile(int start_addr)
|
|||
UDSPInstruction inst = dsp_imem_read(compilePC);
|
||||
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);
|
||||
|
||||
blockSize[start_addr]++;
|
||||
compilePC += opcode->size;
|
||||
|
||||
// If the block was trying to link into itself, remove the link
|
||||
unresolvedJumps[start_addr].remove(compilePC);
|
||||
|
||||
fixup_pc = true;
|
||||
|
||||
// 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
|
||||
// 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;
|
||||
}
|
||||
else
|
||||
{
|
||||
DSPAnalyzer::code_flags[start_addr] |= DSPAnalyzer::CODE_CALL;
|
||||
blockLinks[start_addr] = 0;
|
||||
++unresolvedCalls;
|
||||
|
||||
for(u16 i = 0x0000; i < 0xffff; ++i)
|
||||
{
|
||||
if (!unresolvedJumps[i].empty())
|
||||
{
|
||||
// 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)
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#ifndef _DSPEMITTER_H
|
||||
#define _DSPEMITTER_H
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "DSPCommon.h"
|
||||
#include "x64Emitter.h"
|
||||
|
||||
|
@ -41,7 +43,7 @@ public:
|
|||
|
||||
void CompileDispatcher();
|
||||
const u8 *CompileStub();
|
||||
void Compile(int start_addr);
|
||||
void Compile(u16 start_addr);
|
||||
void ClearCallFlag();
|
||||
|
||||
void Default(UDSPInstruction inst);
|
||||
|
@ -252,6 +254,7 @@ public:
|
|||
u16 startAddr;
|
||||
CompiledCode *blockLinks;
|
||||
u16 *blockSize;
|
||||
std::list<u16> unresolvedJumps[0x10000];
|
||||
|
||||
private:
|
||||
CompiledCode *blocks;
|
||||
|
|
|
@ -162,49 +162,58 @@ void WriteBranchExit(DSPEmitter& emitter)
|
|||
emitter.RET();
|
||||
}
|
||||
|
||||
void WriteBlockLink(DSPEmitter& emitter, u16 dest)
|
||||
{
|
||||
// Jump directly to the called block if it has already been compiled.
|
||||
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
|
||||
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.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));
|
||||
|
||||
// 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
|
||||
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
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
|
||||
WriteBranchExit(emitter);
|
||||
}
|
||||
|
@ -261,48 +270,19 @@ void DSPEmitter::jmprcc(const UDSPInstruction opc)
|
|||
|
||||
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.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
|
||||
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
|
||||
emitter.MOV(64, R(RAX), ImmPtr(&(g_dsp.pc)));
|
||||
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
|
||||
WriteBranchExit(emitter);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue