Skip to content

Commit

Permalink
CPU/Recompiler: Create block links for self-looping blocks
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
stenzek committed Dec 12, 2024
1 parent 2e805d5 commit 99f1332
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 46 deletions.
16 changes: 16 additions & 0 deletions src/core/cpu_code_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/core/cpu_code_cache_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
16 changes: 5 additions & 11 deletions src/core/cpu_recompiler_arm32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
16 changes: 5 additions & 11 deletions src/core/cpu_recompiler_arm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
21 changes: 8 additions & 13 deletions src/core/cpu_recompiler_riscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ using namespace biscuit;
RISCV64Recompiler s_instance;
Recompiler* g_compiler = &s_instance;

} // namespace CPU::Recompiler
} // namespace CPU

bool rvIsCallerSavedRegister(u32 id)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}

Expand Down
15 changes: 4 additions & 11 deletions src/core/cpu_recompiler_x64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down

0 comments on commit 99f1332

Please sign in to comment.