CPU/Recompiler: Support block revalidation instead of flushing
This commit is contained in:
parent
7aafaeacbc
commit
9e82afac7b
|
@ -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)
|
||||||
|
|
|
@ -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,28 +50,26 @@ 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())
|
||||||
goto reexecute_block;
|
{
|
||||||
|
// ensure it's not a self-modifying block
|
||||||
|
if (!block->invalidated || RevalidateBlock(block))
|
||||||
|
goto reexecute_block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: linking
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue