CPU/Recompiler: Implement simple block linking
This commit is contained in:
parent
201be8aa9c
commit
b8de55b9b8
|
@ -11,6 +11,7 @@ namespace CPU {
|
|||
|
||||
bool USE_CODE_CACHE = false;
|
||||
bool USE_RECOMPILER = false;
|
||||
constexpr bool USE_BLOCK_LINKING = true;
|
||||
|
||||
static constexpr size_t RECOMPILER_CODE_CACHE_SIZE = 32 * 1024 * 1024;
|
||||
static constexpr size_t RECOMPILER_FAR_CODE_CACHE_SIZE = 32 * 1024 * 1024;
|
||||
|
@ -32,9 +33,12 @@ void CodeCache::Initialize(System* system, Core* core, Bus* bus)
|
|||
|
||||
void CodeCache::Execute()
|
||||
{
|
||||
if (m_core->m_downcount < 0)
|
||||
return;
|
||||
|
||||
CodeBlockKey next_block_key = GetNextBlockKey();
|
||||
|
||||
while (m_core->m_downcount >= 0)
|
||||
for (;;)
|
||||
{
|
||||
if (m_core->HasPendingInterrupt())
|
||||
{
|
||||
|
@ -48,7 +52,8 @@ void CodeCache::Execute()
|
|||
{
|
||||
Log_WarningPrintf("Falling back to uncached interpreter at 0x%08X", m_core->GetRegs().pc);
|
||||
InterpretUncachedBlock();
|
||||
continue;
|
||||
if (m_core->m_downcount < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -67,20 +72,48 @@ void CodeCache::Execute()
|
|||
LogCurrentState();
|
||||
#endif
|
||||
|
||||
if (m_core->m_downcount < 0)
|
||||
break;
|
||||
else if (m_core->HasPendingInterrupt() || !USE_BLOCK_LINKING)
|
||||
continue;
|
||||
|
||||
next_block_key = GetNextBlockKey();
|
||||
if (next_block_key.bits == block->key.bits)
|
||||
{
|
||||
// we can jump straight to it if there's no pending interrupts
|
||||
if (m_core->m_downcount >= 0 && !m_core->HasPendingInterrupt())
|
||||
{
|
||||
// ensure it's not a self-modifying block
|
||||
if (!block->invalidated || RevalidateBlock(block))
|
||||
goto reexecute_block;
|
||||
}
|
||||
// ensure it's not a self-modifying block
|
||||
if (!block->invalidated || RevalidateBlock(block))
|
||||
goto reexecute_block;
|
||||
}
|
||||
else
|
||||
else if (!block->invalidated)
|
||||
{
|
||||
// TODO: linking
|
||||
// Try to find an already-linked block.
|
||||
// TODO: Don't need to dereference the block, just store a pointer to the code.
|
||||
for (CodeBlock* linked_block : block->link_successors)
|
||||
{
|
||||
if (linked_block->key.bits == next_block_key.bits)
|
||||
{
|
||||
if (linked_block->invalidated && !RevalidateBlock(linked_block))
|
||||
{
|
||||
// CanExecuteBlock can result in a block flush, so stop iterating here.
|
||||
break;
|
||||
}
|
||||
|
||||
// Execute the linked block
|
||||
block = linked_block;
|
||||
goto reexecute_block;
|
||||
}
|
||||
}
|
||||
|
||||
// No acceptable blocks found in the successor list, try a new one.
|
||||
CodeBlock* next_block = LookupBlock(next_block_key);
|
||||
if (next_block)
|
||||
{
|
||||
// Link the previous block to this new block if we find a new block.
|
||||
LinkBlock(block, next_block);
|
||||
block = next_block;
|
||||
goto reexecute_block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -335,6 +368,32 @@ void CodeCache::RemoveBlockFromPageMap(CodeBlock* block)
|
|||
}
|
||||
}
|
||||
|
||||
void CodeCache::LinkBlock(CodeBlock* from, CodeBlock* to)
|
||||
{
|
||||
Log_DebugPrintf("Linking block %p(%08x) to %p(%08x)", from, from->GetPC(), to, to->GetPC());
|
||||
from->link_successors.push_back(to);
|
||||
to->link_predecessors.push_back(from);
|
||||
}
|
||||
|
||||
void CodeCache::UnlinkBlock(CodeBlock* block)
|
||||
{
|
||||
for (CodeBlock* predecessor : block->link_predecessors)
|
||||
{
|
||||
auto iter = std::find(predecessor->link_successors.begin(), predecessor->link_successors.end(), block);
|
||||
Assert(iter != predecessor->link_successors.end());
|
||||
predecessor->link_successors.erase(iter);
|
||||
}
|
||||
block->link_predecessors.clear();
|
||||
|
||||
for (CodeBlock* successor : block->link_successors)
|
||||
{
|
||||
auto iter = std::find(successor->link_predecessors.begin(), successor->link_predecessors.end(), block);
|
||||
Assert(iter != successor->link_predecessors.end());
|
||||
successor->link_predecessors.erase(iter);
|
||||
}
|
||||
block->link_successors.clear();
|
||||
}
|
||||
|
||||
void CodeCache::InterpretCachedBlock(const CodeBlock& block)
|
||||
{
|
||||
// set up the state so we've already fetched the instruction
|
||||
|
|
|
@ -36,13 +36,27 @@ private:
|
|||
|
||||
void LogCurrentState();
|
||||
|
||||
/// Returns the block key for the current execution state.
|
||||
CodeBlockKey GetNextBlockKey() const;
|
||||
|
||||
/// Looks up the block in the cache if it's already been compiled.
|
||||
CodeBlock* LookupBlock(CodeBlockKey key);
|
||||
|
||||
/// Can the current block execute? This will re-validate the block if necessary.
|
||||
/// The block can also be flushed if recompilation failed, so ignore the pointer if false is returned.
|
||||
bool RevalidateBlock(CodeBlock* block);
|
||||
|
||||
bool CompileBlock(CodeBlock* block);
|
||||
void FlushBlock(CodeBlock* block);
|
||||
void AddBlockToPageMap(CodeBlock* block);
|
||||
void RemoveBlockFromPageMap(CodeBlock* block);
|
||||
|
||||
/// Link block from to to.
|
||||
void LinkBlock(CodeBlock* from, CodeBlock* to);
|
||||
|
||||
/// Unlink all blocks which point to this block, and any that this block links to.
|
||||
void UnlinkBlock(CodeBlock* block);
|
||||
|
||||
void InterpretCachedBlock(const CodeBlock& block);
|
||||
void InterpretUncachedBlock();
|
||||
|
||||
|
|
|
@ -418,15 +418,17 @@ struct CodeBlockInstruction
|
|||
|
||||
struct CodeBlock
|
||||
{
|
||||
using HostCodePointer = void (*)(Core*);
|
||||
|
||||
CodeBlock(const CodeBlockKey key_) : key(key_) {}
|
||||
|
||||
CodeBlockKey key;
|
||||
u32 host_code_size = 0;
|
||||
HostCodePointer host_code = nullptr;
|
||||
|
||||
std::vector<CodeBlockInstruction> instructions;
|
||||
|
||||
using HostCodePointer = void (*)(Core*);
|
||||
HostCodePointer host_code = nullptr;
|
||||
u32 host_code_size = 0;
|
||||
std::vector<CodeBlock*> link_predecessors;
|
||||
std::vector<CodeBlock*> link_successors;
|
||||
|
||||
bool invalidated = false;
|
||||
|
||||
|
|
Loading…
Reference in New Issue