CPU/Recompiler: Support block revalidation instead of flushing

This commit is contained in:
Connor McLaughlin 2019-11-22 00:32:40 +10:00
parent 7aafaeacbc
commit 9e82afac7b
4 changed files with 117 additions and 60 deletions

View File

@ -523,7 +523,7 @@ void Bus::DoWriteSPU(MemoryAccessSize size, u32 offset, u32 value)
void Bus::DoInvalidateCodeCache(u32 page_index) void Bus::DoInvalidateCodeCache(u32 page_index)
{ {
m_cpu_code_cache->FlushBlocksWithPageIndex(page_index); m_cpu_code_cache->InvalidateBlocksWithPageIndex(page_index);
} }
u32 Bus::DoReadDMA(MemoryAccessSize size, u32 offset) u32 Bus::DoReadDMA(MemoryAccessSize size, u32 offset)

View File

@ -40,8 +40,8 @@ void CodeCache::Execute()
next_block_key = GetNextBlockKey(); next_block_key = GetNextBlockKey();
} }
m_current_block = LookupBlock(next_block_key); CodeBlock* block = LookupBlock(next_block_key);
if (!m_current_block) if (!block)
{ {
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();
@ -50,30 +50,28 @@ void CodeCache::Execute()
reexecute_block: reexecute_block:
if (USE_RECOMPILER) if (USE_RECOMPILER)
m_current_block->host_code(m_core); block->host_code(m_core);
else else
InterpretCachedBlock(*m_current_block); InterpretCachedBlock(*block);
//LogCurrentState(); // LogCurrentState();
next_block_key = GetNextBlockKey(); next_block_key = GetNextBlockKey();
if (m_current_block_flushed) if (next_block_key.bits == block->key.bits)
{
m_current_block_flushed = false;
delete m_current_block;
m_current_block = nullptr;
continue;
}
// Loop to same block?
next_block_key = GetNextBlockKey();
if (next_block_key.bits == m_current_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()) 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; goto reexecute_block;
} }
} }
else
{
// TODO: linking
}
}
} }
void CodeCache::Reset() void CodeCache::Reset()
@ -109,37 +107,69 @@ CodeBlockKey CodeCache::GetNextBlockKey() const
return key; return key;
} }
const CPU::CodeBlock* CodeCache::LookupBlock(CodeBlockKey key) CodeBlock* CodeCache::LookupBlock(CodeBlockKey key)
{ {
BlockMap::iterator iter = m_blocks.find(key.bits); BlockMap::iterator iter = m_blocks.find(key.bits);
if (iter != m_blocks.end()) if (iter != m_blocks.end())
return iter->second; {
// ensure it hasn't been invalidated
CodeBlock* existing_block = iter->second;
if (!existing_block || !existing_block->invalidated || RevalidateBlock(existing_block))
return existing_block;
}
CodeBlock* block = new CodeBlock(); CodeBlock* block = new CodeBlock(key);
block->key = key;
if (CompileBlock(block)) if (CompileBlock(block))
{ {
// insert into the page map // add it to the page map if it's in ram
if (m_bus->IsRAMAddress(key.GetPC())) AddBlockToPageMap(block);
{
const u32 start_page = block->GetStartPageIndex();
const u32 end_page = block->GetEndPageIndex();
for (u32 page = start_page; page < end_page; page++)
{
m_ram_block_map[page].push_back(block);
m_bus->SetRAMCodePage(page);
}
}
} }
else else
{ {
Log_ErrorPrintf("Failed to compile block at PC=0x%08X", key.GetPC()); Log_ErrorPrintf("Failed to compile block at PC=0x%08X", key.GetPC());
delete block;
block = nullptr;
} }
iter = m_blocks.emplace(key.bits, block).first; iter = m_blocks.emplace(key.bits, block).first;
return block; return block;
} }
bool CodeCache::RevalidateBlock(CodeBlock* block)
{
for (const CodeBlockInstruction& cbi : block->instructions)
{
u32 new_code = 0;
m_bus->DispatchAccess<MemoryAccessType::Read, MemoryAccessSize::Word>(cbi.pc, new_code);
if (cbi.instruction.bits != new_code)
{
Log_DebugPrintf("Block 0x%08X changed at PC 0x%08X - %08X to %08X - recompiling.", block->GetPC(), cbi.pc,
cbi.instruction.bits, new_code);
goto recompile;
}
}
// re-add it to the page map since it's still up-to-date
block->invalidated = false;
AddBlockToPageMap(block);
return true;
recompile:
block->instructions.clear();
if (!CompileBlock(block))
{
Log_WarningPrintf("Failed to recompile block 0x%08X - flushing.", block->GetPC());
FlushBlock(block);
return false;
}
// re-add to page map again
if (block->IsInRAM())
AddBlockToPageMap(block);
return true;
}
bool CodeCache::CompileBlock(CodeBlock* block) bool CodeCache::CompileBlock(CodeBlock* block)
{ {
u32 pc = block->GetPC(); u32 pc = block->GetPC();
@ -226,13 +256,19 @@ bool CodeCache::CompileBlock(CodeBlock* block)
return true; return true;
} }
void CodeCache::FlushBlocksWithPageIndex(u32 page_index) void CodeCache::InvalidateBlocksWithPageIndex(u32 page_index)
{ {
DebugAssert(page_index < CPU_CODE_CACHE_PAGE_COUNT); DebugAssert(page_index < CPU_CODE_CACHE_PAGE_COUNT);
auto& blocks = m_ram_block_map[page_index]; auto& blocks = m_ram_block_map[page_index];
while (!blocks.empty()) for (CodeBlock* block : blocks)
FlushBlock(blocks.back()); {
// Invalidate forces the block to be checked again.
Log_DebugPrintf("Invalidating block at 0x%08X", block->GetPC());
block->invalidated = true;
}
// Block will be re-added next execution.
blocks.clear();
m_bus->ClearRAMCodePage(page_index); m_bus->ClearRAMCodePage(page_index);
} }
@ -242,7 +278,33 @@ void CodeCache::FlushBlock(CodeBlock* block)
Assert(iter != m_blocks.end() && iter->second == block); Assert(iter != m_blocks.end() && iter->second == block);
Log_DevPrintf("Flushing block at address 0x%08X", block->GetPC()); Log_DevPrintf("Flushing block at address 0x%08X", block->GetPC());
// remove from the page map // if it's been invalidated it won't be in the page map
if (block->invalidated)
RemoveBlockFromPageMap(block);
m_blocks.erase(iter);
delete block;
}
void CodeCache::AddBlockToPageMap(CodeBlock* block)
{
if (!block->IsInRAM())
return;
const u32 start_page = block->GetStartPageIndex();
const u32 end_page = block->GetEndPageIndex();
for (u32 page = start_page; page < end_page; page++)
{
m_ram_block_map[page].push_back(block);
m_bus->SetRAMCodePage(page);
}
}
void CodeCache::RemoveBlockFromPageMap(CodeBlock* block)
{
if (!block->IsInRAM())
return;
const u32 start_page = block->GetStartPageIndex(); const u32 start_page = block->GetStartPageIndex();
const u32 end_page = block->GetEndPageIndex(); const u32 end_page = block->GetEndPageIndex();
for (u32 page = start_page; page < end_page; page++) for (u32 page = start_page; page < end_page; page++)
@ -252,20 +314,6 @@ void CodeCache::FlushBlock(CodeBlock* block)
Assert(page_block_iter != page_blocks.end()); Assert(page_block_iter != page_blocks.end());
page_blocks.erase(page_block_iter); page_blocks.erase(page_block_iter);
} }
// remove from block map
m_blocks.erase(iter);
// flushing block currently executing?
if (m_current_block == block)
{
Log_WarningPrintf("Flushing currently-executing block 0x%08X", block->GetPC());
m_current_block_flushed = true;
}
else
{
delete block;
}
} }
void CodeCache::InterpretCachedBlock(const CodeBlock& block) void CodeCache::InterpretCachedBlock(const CodeBlock& block)

View File

@ -28,8 +28,8 @@ public:
void Reset(); void Reset();
void Execute(); void Execute();
/// Flushes all blocks which are in the range of the specified code page. /// Invalidates all blocks which are in the range of the specified code page.
void FlushBlocksWithPageIndex(u32 page_index); void InvalidateBlocksWithPageIndex(u32 page_index);
private: private:
using BlockMap = std::unordered_map<u32, CodeBlock*>; using BlockMap = std::unordered_map<u32, CodeBlock*>;
@ -37,9 +37,12 @@ private:
void LogCurrentState(); void LogCurrentState();
CodeBlockKey GetNextBlockKey() const; CodeBlockKey GetNextBlockKey() const;
const CodeBlock* LookupBlock(CodeBlockKey key); CodeBlock* LookupBlock(CodeBlockKey key);
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 RemoveBlockFromPageMap(CodeBlock* block);
void InterpretCachedBlock(const CodeBlock& block); void InterpretCachedBlock(const CodeBlock& block);
void InterpretUncachedBlock(); void InterpretUncachedBlock();
@ -47,9 +50,6 @@ private:
Core* m_core; Core* m_core;
Bus* m_bus; Bus* m_bus;
const CodeBlock* m_current_block = nullptr;
bool m_current_block_flushed = false;
std::unique_ptr<JitCodeBuffer> m_code_buffer; std::unique_ptr<JitCodeBuffer> m_code_buffer;
std::unique_ptr<Recompiler::ASMFunctions> m_asm_functions; std::unique_ptr<Recompiler::ASMFunctions> m_asm_functions;

View File

@ -410,13 +410,17 @@ struct CodeBlockInstruction
struct CodeBlock struct CodeBlock
{ {
CodeBlock(const CodeBlockKey key_) : key(key_) {}
CodeBlockKey key; CodeBlockKey key;
std::vector<CodeBlockInstruction> instructions; std::vector<CodeBlockInstruction> instructions;
using HostCodePointer = void(*)(Core*); using HostCodePointer = void (*)(Core*);
HostCodePointer host_code; HostCodePointer host_code = nullptr;
u32 host_code_size; u32 host_code_size = 0;
bool invalidated = false;
const u32 GetPC() const { return key.GetPC(); } const u32 GetPC() const { return key.GetPC(); }
const u32 GetSizeInBytes() const { return static_cast<u32>(instructions.size()) * sizeof(Instruction); } const u32 GetSizeInBytes() const { return static_cast<u32>(instructions.size()) * sizeof(Instruction); }
@ -425,6 +429,11 @@ struct CodeBlock
{ {
return ((key.GetPC() + GetSizeInBytes() + (CPU_CODE_CACHE_PAGE_SIZE - 1)) / CPU_CODE_CACHE_PAGE_SIZE); return ((key.GetPC() + GetSizeInBytes() + (CPU_CODE_CACHE_PAGE_SIZE - 1)) / CPU_CODE_CACHE_PAGE_SIZE);
} }
bool IsInRAM() const
{
// TODO: Constant
return key.GetPC() < 0x200000;
}
}; };
} // namespace CPU } // namespace CPU