mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-29 17:15:40 +00:00
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_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
|
// ensure it's not a self-modifying block
|
||||||
if (!block->invalidated || RevalidateBlock(block))
|
if (!block->invalidated || RevalidateBlock(block))
|
||||||
goto reexecute_block;
|
goto reexecute_block;
|
||||||
}
|
}
|
||||||
}
|
else if (!block->invalidated)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// 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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue