From 99f133223cb39d39ee7580ccad24e2eeb863efc4 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 12 Dec 2024 16:27:59 +1000 Subject: [PATCH] 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. --- src/core/cpu_code_cache.cpp | 16 ++++++++++++++++ src/core/cpu_code_cache_private.h | 1 + src/core/cpu_recompiler_arm32.cpp | 16 +++++----------- src/core/cpu_recompiler_arm64.cpp | 16 +++++----------- src/core/cpu_recompiler_riscv64.cpp | 21 ++++++++------------- src/core/cpu_recompiler_x64.cpp | 15 ++++----------- 6 files changed, 39 insertions(+), 46 deletions(-) diff --git a/src/core/cpu_code_cache.cpp b/src/core/cpu_code_cache.cpp index f6f845eef..681401106 100644 --- a/src/core/cpu_code_cache.cpp +++ b/src/core/cpu_code_cache.cpp @@ -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) diff --git a/src/core/cpu_code_cache_private.h b/src/core/cpu_code_cache_private.h index 2981ec418..46f639f82 100644 --- a/src/core/cpu_code_cache_private.h +++ b/src/core/cpu_code_cache_private.h @@ -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, diff --git a/src/core/cpu_recompiler_arm32.cpp b/src/core/cpu_recompiler_arm32.cpp index 6338e701e..07657a34f 100644 --- a/src/core/cpu_recompiler_arm32.cpp +++ b/src/core/cpu_recompiler_arm32.cpp @@ -741,17 +741,11 @@ void CPU::ARM32Recompiler::EndAndLinkBlock(const std::optional& 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(), true); - } - else - { - const void* target = CodeCache::CreateBlockLink(m_block, armAsm->GetCursorAddress(), newpc.value()); - armEmitJmp(armAsm, target, true); - } + const void* target = (newpc.value() == m_block->pc) ? + CodeCache::CreateSelfBlockLink(m_block, armAsm->GetCursorAddress(), + armAsm->GetBuffer()->GetStartAddress()) : + CodeCache::CreateBlockLink(m_block, armAsm->GetCursorAddress(), newpc.value()); + armEmitJmp(armAsm, target, true); } } diff --git a/src/core/cpu_recompiler_arm64.cpp b/src/core/cpu_recompiler_arm64.cpp index 7ceb6531c..f949435e5 100644 --- a/src/core/cpu_recompiler_arm64.cpp +++ b/src/core/cpu_recompiler_arm64.cpp @@ -901,17 +901,11 @@ void CPU::ARM64Recompiler::EndAndLinkBlock(const std::optional& 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(), true); - } - else - { - const void* target = CodeCache::CreateBlockLink(m_block, armAsm->GetCursorAddress(), newpc.value()); - armEmitJmp(armAsm, target, true); - } + const void* target = (newpc.value() == m_block->pc) ? + CodeCache::CreateSelfBlockLink(m_block, armAsm->GetCursorAddress(), + armAsm->GetBuffer()->GetStartAddress()) : + CodeCache::CreateBlockLink(m_block, armAsm->GetCursorAddress(), newpc.value()); + armEmitJmp(armAsm, target, true); } } diff --git a/src/core/cpu_recompiler_riscv64.cpp b/src/core/cpu_recompiler_riscv64.cpp index c3fad7499..f11640579 100644 --- a/src/core/cpu_recompiler_riscv64.cpp +++ b/src/core/cpu_recompiler_riscv64.cpp @@ -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& 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); } } diff --git a/src/core/cpu_recompiler_x64.cpp b/src/core/cpu_recompiler_x64.cpp index 7351eb8ed..2beefc633 100644 --- a/src/core/cpu_recompiler_x64.cpp +++ b/src/core/cpu_recompiler_x64.cpp @@ -629,17 +629,10 @@ void CPU::X64Recompiler::EndAndLinkBlock(const std::optional& 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(), newpc.value()); - cg->jmp(target, CodeGenerator::T_NEAR); - } + const void* target = (newpc.value() == m_block->pc) ? + CodeCache::CreateSelfBlockLink(m_block, cg->getCurr(), cg->getCode()) : + CodeCache::CreateBlockLink(m_block, cg->getCurr(), newpc.value()); + cg->jmp(target, CodeGenerator::T_NEAR); } }