CPU/Recompiler: Create block links for self-looping blocks

This way invalidation will rewrite the jump back to the compiler.
Otherwise a SMC block can end up looping itself indefinitely.

Might help with Spyro 2/3. I can't seem to make them crash anymore.
This commit is contained in:
Stenzek 2024-12-12 16:27:59 +10:00
parent 2e805d56dd
commit 99f133223c
No known key found for this signature in database
6 changed files with 39 additions and 46 deletions

View File

@ -1421,6 +1421,22 @@ const void* CPU::CodeCache::CreateBlockLink(Block* block, void* code, u32 newpc)
return dst;
}
const void* CPU::CodeCache::CreateSelfBlockLink(Block* block, void* code, const void* block_start)
{
const void* dst = g_dispatcher;
if (g_settings.cpu_recompiler_block_linking)
{
dst = block_start;
BlockLinkMap::iterator iter = s_block_links.emplace(block->pc, code);
DebugAssert(block->num_exit_links < MAX_BLOCK_EXIT_LINKS);
block->exit_links[block->num_exit_links++] = iter;
}
DEBUG_LOG("Self linking {} with dst pc {:08X} to {}", code, block->pc, dst);
return dst;
}
void CPU::CodeCache::BacklinkBlocks(u32 pc, const void* dst)
{
if (!g_settings.cpu_recompiler_block_linking)

View File

@ -239,6 +239,7 @@ const void* GetInterpretUncachedBlockFunction();
void CompileOrRevalidateBlock(u32 start_pc);
void DiscardAndRecompileBlock(u32 start_pc);
const void* CreateBlockLink(Block* from_block, void* code, u32 newpc);
const void* CreateSelfBlockLink(Block* block, void* code, const void* block_start);
void AddLoadStoreInfo(void* code_address, u32 code_size, u32 guest_pc, const void* thunk_address);
void AddLoadStoreInfo(void* code_address, u32 code_size, u32 guest_pc, u32 guest_block, TickCount cycles,

View File

@ -741,17 +741,11 @@ void CPU::ARM32Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool
}
else
{
if (newpc.value() == m_block->pc)
{
// Special case: ourselves! No need to backlink then.
DEBUG_LOG("Linking block at {:08X} to self", m_block->pc);
armEmitJmp(armAsm, armAsm->GetBuffer()->GetStartAddress<const void*>(), true);
}
else
{
const void* target = CodeCache::CreateBlockLink(m_block, armAsm->GetCursorAddress<void*>(), newpc.value());
armEmitJmp(armAsm, target, true);
}
const void* target = (newpc.value() == m_block->pc) ?
CodeCache::CreateSelfBlockLink(m_block, armAsm->GetCursorAddress<void*>(),
armAsm->GetBuffer()->GetStartAddress<const void*>()) :
CodeCache::CreateBlockLink(m_block, armAsm->GetCursorAddress<void*>(), newpc.value());
armEmitJmp(armAsm, target, true);
}
}

View File

@ -901,17 +901,11 @@ void CPU::ARM64Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool
}
else
{
if (newpc.value() == m_block->pc)
{
// Special case: ourselves! No need to backlink then.
DEBUG_LOG("Linking block at {:08X} to self", m_block->pc);
armEmitJmp(armAsm, armAsm->GetBuffer()->GetStartAddress<const void*>(), true);
}
else
{
const void* target = CodeCache::CreateBlockLink(m_block, armAsm->GetCursorAddress<void*>(), newpc.value());
armEmitJmp(armAsm, target, true);
}
const void* target = (newpc.value() == m_block->pc) ?
CodeCache::CreateSelfBlockLink(m_block, armAsm->GetCursorAddress<void*>(),
armAsm->GetBuffer()->GetStartAddress<const void*>()) :
CodeCache::CreateBlockLink(m_block, armAsm->GetCursorAddress<void*>(), newpc.value());
armEmitJmp(armAsm, target, true);
}
}

View File

@ -66,7 +66,7 @@ using namespace biscuit;
RISCV64Recompiler s_instance;
Recompiler* g_compiler = &s_instance;
} // namespace CPU::Recompiler
} // namespace CPU
bool rvIsCallerSavedRegister(u32 id)
{
@ -150,7 +150,8 @@ void rvEmitFarLoad(biscuit::Assembler* rvAsm, const biscuit::GPR& reg, const voi
rvAsm->LWU(reg, lo, reg);
}
[[maybe_unused]] void rvEmitFarStore(biscuit::Assembler* rvAsm, const biscuit::GPR& reg, const void* addr, const biscuit::GPR& tempreg)
[[maybe_unused]] void rvEmitFarStore(biscuit::Assembler* rvAsm, const biscuit::GPR& reg, const void* addr,
const biscuit::GPR& tempreg)
{
const auto [hi, lo] = rvGetAddressImmediates(rvAsm->GetCursorPointer(), addr);
rvAsm->AUIPC(tempreg, hi);
@ -697,17 +698,11 @@ void CPU::RISCV64Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bo
}
else
{
if (newpc.value() == m_block->pc)
{
// Special case: ourselves! No need to backlink then.
DEBUG_LOG("Linking block at {:08X} to self", m_block->pc);
rvEmitJmp(rvAsm, rvAsm->GetBufferPointer(0));
}
else
{
const void* target = CreateBlockLink(m_block, rvAsm->GetCursorPointer(), newpc.value());
rvEmitJmp(rvAsm, target);
}
const void* target =
(newpc.value() == m_block->pc) ?
CodeCache::CreateSelfBlockLink(m_block, rvAsm->GetCursorPointer(), rvAsm->GetBufferPointer(0)) :
CodeCache::CreateBlockLink(m_block, rvAsm->GetCursorPointer(), newpc.value());
rvEmitJmp(rvAsm, target);
}
}

View File

@ -629,17 +629,10 @@ void CPU::X64Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool d
}
else
{
if (newpc.value() == m_block->pc)
{
// Special case: ourselves! No need to backlink then.
DEBUG_LOG("Linking block at {:08X} to self", m_block->pc);
cg->jmp(cg->getCode());
}
else
{
const void* target = CodeCache::CreateBlockLink(m_block, cg->getCurr<void*>(), newpc.value());
cg->jmp(target, CodeGenerator::T_NEAR);
}
const void* target = (newpc.value() == m_block->pc) ?
CodeCache::CreateSelfBlockLink(m_block, cg->getCurr<void*>(), cg->getCode()) :
CodeCache::CreateBlockLink(m_block, cg->getCurr<void*>(), newpc.value());
cg->jmp(target, CodeGenerator::T_NEAR);
}
}