CPU/Recompiler: Implement simple block linking

This commit is contained in:
Connor McLaughlin 2019-11-23 13:16:43 +10:00
parent 201be8aa9c
commit b8de55b9b8
3 changed files with 89 additions and 14 deletions

View file

@ -11,6 +11,7 @@ namespace CPU {
bool USE_CODE_CACHE = false; bool USE_CODE_CACHE = false;
bool USE_RECOMPILER = 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_CODE_CACHE_SIZE = 32 * 1024 * 1024;
static constexpr size_t RECOMPILER_FAR_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() void CodeCache::Execute()
{ {
if (m_core->m_downcount < 0)
return;
CodeBlockKey next_block_key = GetNextBlockKey(); CodeBlockKey next_block_key = GetNextBlockKey();
while (m_core->m_downcount >= 0) for (;;)
{ {
if (m_core->HasPendingInterrupt()) 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); Log_WarningPrintf("Falling back to uncached interpreter at 0x%08X", m_core->GetRegs().pc);
InterpretUncachedBlock(); InterpretUncachedBlock();
continue; if (m_core->m_downcount < 0)
break;
} }
#if 0 #if 0
@ -67,20 +72,48 @@ void CodeCache::Execute()
LogCurrentState(); LogCurrentState();
#endif #endif
if (m_core->m_downcount < 0)
break;
else if (m_core->HasPendingInterrupt() || !USE_BLOCK_LINKING)
continue;
next_block_key = GetNextBlockKey(); next_block_key = GetNextBlockKey();
if (next_block_key.bits == block->key.bits) if (next_block_key.bits == block->key.bits)
{ {
// we can jump straight to it if there's no pending interrupts // 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))
// ensure it's not a self-modifying block goto reexecute_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) void CodeCache::InterpretCachedBlock(const CodeBlock& block)
{ {
// set up the state so we've already fetched the instruction // set up the state so we've already fetched the instruction

View file

@ -36,13 +36,27 @@ private:
void LogCurrentState(); void LogCurrentState();
/// Returns the block key for the current execution state.
CodeBlockKey GetNextBlockKey() const; CodeBlockKey GetNextBlockKey() const;
/// Looks up the block in the cache if it's already been compiled.
CodeBlock* LookupBlock(CodeBlockKey key); 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 RevalidateBlock(CodeBlock* block);
bool CompileBlock(CodeBlock* block); bool CompileBlock(CodeBlock* block);
void FlushBlock(CodeBlock* block); void FlushBlock(CodeBlock* block);
void AddBlockToPageMap(CodeBlock* block); void AddBlockToPageMap(CodeBlock* block);
void RemoveBlockFromPageMap(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 InterpretCachedBlock(const CodeBlock& block);
void InterpretUncachedBlock(); void InterpretUncachedBlock();

View file

@ -418,15 +418,17 @@ struct CodeBlockInstruction
struct CodeBlock struct CodeBlock
{ {
using HostCodePointer = void (*)(Core*);
CodeBlock(const CodeBlockKey key_) : key(key_) {} CodeBlock(const CodeBlockKey key_) : key(key_) {}
CodeBlockKey key; CodeBlockKey key;
u32 host_code_size = 0;
HostCodePointer host_code = nullptr;
std::vector<CodeBlockInstruction> instructions; std::vector<CodeBlockInstruction> instructions;
std::vector<CodeBlock*> link_predecessors;
using HostCodePointer = void (*)(Core*); std::vector<CodeBlock*> link_successors;
HostCodePointer host_code = nullptr;
u32 host_code_size = 0;
bool invalidated = false; bool invalidated = false;