CPU/CodeCache: Impove overflow handling

This commit is contained in:
Connor McLaughlin 2022-11-05 14:43:52 +10:00
parent bdde3ce07b
commit 601e3586b2

View file

@ -195,13 +195,13 @@ void LogCurrentState();
static CodeBlockKey GetNextBlockKey(); static CodeBlockKey GetNextBlockKey();
/// Looks up the block in the cache if it's already been compiled. /// Looks up the block in the cache if it's already been compiled.
static CodeBlock* LookupBlock(CodeBlockKey key); static CodeBlock* LookupBlock(CodeBlockKey key, bool allow_flush);
/// Can the current block execute? This will re-validate the block if necessary. /// 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. /// The block can also be flushed if recompilation failed, so ignore the pointer if false is returned.
static bool RevalidateBlock(CodeBlock* block); static bool RevalidateBlock(CodeBlock* block, bool allow_flush);
static bool CompileBlock(CodeBlock* block); static bool CompileBlock(CodeBlock* block, bool allow_flush);
static void RemoveReferencesToBlock(CodeBlock* block); static void RemoveReferencesToBlock(CodeBlock* block);
static void AddBlockToPageMap(CodeBlock* block); static void AddBlockToPageMap(CodeBlock* block);
static void RemoveBlockFromPageMap(CodeBlock* block); static void RemoveBlockFromPageMap(CodeBlock* block);
@ -310,7 +310,7 @@ static void ExecuteImpl()
next_block_key = GetNextBlockKey(); next_block_key = GetNextBlockKey();
while (g_state.pending_ticks < g_state.downcount) while (g_state.pending_ticks < g_state.downcount)
{ {
CodeBlock* block = LookupBlock(next_block_key); CodeBlock* block = LookupBlock(next_block_key, true);
if (!block) if (!block)
{ {
InterpretUncachedBlock<pgxp_mode>(); InterpretUncachedBlock<pgxp_mode>();
@ -346,7 +346,7 @@ static void ExecuteImpl()
{ {
// we can jump straight to it if there's no pending interrupts // we can jump straight to it if there's no pending interrupts
// 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, true))
goto reexecute_block; goto reexecute_block;
} }
else if (!block->invalidated) else if (!block->invalidated)
@ -358,7 +358,7 @@ static void ExecuteImpl()
CodeBlock* linked_block = li.block; CodeBlock* linked_block = li.block;
if (linked_block->key.bits == next_block_key.bits) if (linked_block->key.bits == next_block_key.bits)
{ {
if (linked_block->invalidated && !RevalidateBlock(linked_block)) if (linked_block->invalidated && !RevalidateBlock(linked_block, true))
{ {
// CanExecuteBlock can result in a block flush, so stop iterating here. // CanExecuteBlock can result in a block flush, so stop iterating here.
break; break;
@ -371,7 +371,7 @@ static void ExecuteImpl()
} }
// No acceptable blocks found in the successor list, try a new one. // No acceptable blocks found in the successor list, try a new one.
CodeBlock* next_block = LookupBlock(next_block_key); CodeBlock* next_block = LookupBlock(next_block_key, false);
if (next_block) if (next_block)
{ {
// Link the previous block to this new block if we find a new block. // Link the previous block to this new block if we find a new block.
@ -537,7 +537,7 @@ static void FallbackExistingBlockToInterpreter(CodeBlock* block)
delete block; delete block;
} }
CodeBlock* LookupBlock(CodeBlockKey key) CodeBlock* LookupBlock(CodeBlockKey key, bool allow_flush)
{ {
BlockMap::iterator iter = s_blocks.find(key.bits); BlockMap::iterator iter = s_blocks.find(key.bits);
if (iter != s_blocks.end()) if (iter != s_blocks.end())
@ -548,7 +548,7 @@ CodeBlock* LookupBlock(CodeBlockKey key)
return existing_block; return existing_block;
// if compilation fails or we're forced back to the interpreter, bail out // if compilation fails or we're forced back to the interpreter, bail out
if (RevalidateBlock(existing_block)) if (RevalidateBlock(existing_block, allow_flush))
return existing_block; return existing_block;
else else
return nullptr; return nullptr;
@ -557,7 +557,7 @@ CodeBlock* LookupBlock(CodeBlockKey key)
CodeBlock* block = new CodeBlock(key); CodeBlock* block = new CodeBlock(key);
block->recompile_frame_number = System::GetFrameNumber(); block->recompile_frame_number = System::GetFrameNumber();
if (CompileBlock(block)) if (CompileBlock(block, allow_flush))
{ {
// add it to the page map if it's in ram // add it to the page map if it's in ram
AddBlockToPageMap(block); AddBlockToPageMap(block);
@ -574,11 +574,13 @@ CodeBlock* LookupBlock(CodeBlockKey key)
block = nullptr; block = nullptr;
} }
if (block || allow_flush)
s_blocks.emplace(key.bits, block); s_blocks.emplace(key.bits, block);
return block; return block;
} }
bool RevalidateBlock(CodeBlock* block) bool RevalidateBlock(CodeBlock* block, bool allow_flush)
{ {
for (const CodeBlockInstruction& cbi : block->instructions) for (const CodeBlockInstruction& cbi : block->instructions)
{ {
@ -634,7 +636,7 @@ recompile:
block->instructions.clear(); block->instructions.clear();
if (!CompileBlock(block)) if (!CompileBlock(block, allow_flush))
{ {
Log_PerfPrintf("Failed to recompile block 0x%08X, falling back to interpreter.", block->GetPC()); Log_PerfPrintf("Failed to recompile block 0x%08X, falling back to interpreter.", block->GetPC());
FallbackExistingBlockToInterpreter(block); FallbackExistingBlockToInterpreter(block);
@ -657,7 +659,7 @@ recompile:
return true; return true;
} }
bool CompileBlock(CodeBlock* block) bool CompileBlock(CodeBlock* block, bool allow_flush)
{ {
u32 pc = block->GetPC(); u32 pc = block->GetPC();
bool is_branch_delay_slot = false; bool is_branch_delay_slot = false;
@ -776,10 +778,18 @@ bool CompileBlock(CodeBlock* block)
(block->instructions.size() * Recompiler::MAX_NEAR_HOST_BYTES_PER_INSTRUCTION) || (block->instructions.size() * Recompiler::MAX_NEAR_HOST_BYTES_PER_INSTRUCTION) ||
s_code_buffer.GetFreeFarCodeSpace() < s_code_buffer.GetFreeFarCodeSpace() <
(block->instructions.size() * Recompiler::MAX_FAR_HOST_BYTES_PER_INSTRUCTION)) (block->instructions.size() * Recompiler::MAX_FAR_HOST_BYTES_PER_INSTRUCTION))
{
if (allow_flush)
{ {
Log_WarningPrintf("Out of code space, flushing all blocks."); Log_WarningPrintf("Out of code space, flushing all blocks.");
Flush(); Flush();
} }
else
{
Log_ErrorPrintf("Out of code space and cannot flush while compiling %08X.", block->GetPC());
return false;
}
}
s_code_buffer.WriteProtect(false); s_code_buffer.WriteProtect(false);
Recompiler::CodeGenerator codegen(&s_code_buffer); Recompiler::CodeGenerator codegen(&s_code_buffer);
@ -801,7 +811,7 @@ bool CompileBlock(CodeBlock* block)
void FastCompileBlockFunction() void FastCompileBlockFunction()
{ {
CodeBlock* block = LookupBlock(GetNextBlockKey()); CodeBlock* block = LookupBlock(GetNextBlockKey(), true);
if (block) if (block)
{ {
s_single_block_asm_dispatcher(block->host_code); s_single_block_asm_dispatcher(block->host_code);
@ -1218,9 +1228,9 @@ void CPU::Recompiler::Thunks::ResolveBranch(CodeBlock* block, void* host_pc, voi
using namespace CPU::CodeCache; using namespace CPU::CodeCache;
CodeBlockKey key = GetNextBlockKey(); CodeBlockKey key = GetNextBlockKey();
CodeBlock* successor_block = LookupBlock(key); CodeBlock* successor_block = LookupBlock(key, false);
if (!successor_block || (successor_block->invalidated && !RevalidateBlock(successor_block)) || !block->can_link || if (!successor_block || (successor_block->invalidated && !RevalidateBlock(successor_block, false)) ||
!successor_block->can_link) !block->can_link || !successor_block->can_link)
{ {
// just turn it into a return to the dispatcher instead. // just turn it into a return to the dispatcher instead.
s_code_buffer.WriteProtect(false); s_code_buffer.WriteProtect(false);