CPU/NewRec: Handle inside-block SMC

i.e. Spyro 2/3 PAL.
This commit is contained in:
Stenzek 2023-11-21 13:26:17 +10:00
parent 20de40a597
commit e10ff550d5
No known key found for this signature in database
6 changed files with 37 additions and 2 deletions

View file

@ -1602,11 +1602,13 @@ void CPU::NewRec::Compiler::CompileLoadStoreTemplate(void (Compiler::*func)(Comp
// constant address?
std::optional<VirtualMemoryAddress> addr;
std::optional<VirtualMemoryAddress> spec_addr;
bool use_fastmem = CodeCache::IsUsingFastmem() && !g_settings.cpu_recompiler_memory_exceptions &&
!SpecIsCacheIsolated() && !CodeCache::HasPreviouslyFaultedOnPC(m_current_instruction_pc);
if (HasConstantReg(rs))
{
addr = GetConstantRegU32(rs) + inst->i.imm_sext32();
spec_addr = addr;
cf.const_s = true;
if (!Bus::CanUseFastmemForAddress(addr.value()))
@ -1617,7 +1619,7 @@ void CPU::NewRec::Compiler::CompileLoadStoreTemplate(void (Compiler::*func)(Comp
}
else
{
const std::optional<VirtualMemoryAddress> spec_addr = SpecExec_LoadStoreAddr();
spec_addr = SpecExec_LoadStoreAddr();
if (use_fastmem && spec_addr.has_value() && !Bus::CanUseFastmemForAddress(spec_addr.value()))
{
Log_DebugFmt("Not using fastmem for speculative {:08X}", spec_addr.value());
@ -1671,6 +1673,22 @@ void CPU::NewRec::Compiler::CompileLoadStoreTemplate(void (Compiler::*func)(Comp
}
(this->*func)(cf, size, sign, use_fastmem, addr);
if (store && !m_block_ended && !m_current_instruction_branch_delay_slot && spec_addr.has_value() &&
GetSegmentForAddress(spec_addr.value()) != Segment::KSEG2)
{
// Get rid of physical aliases.
const u32 phys_spec_addr = VirtualAddressToPhysical(spec_addr.value());
if (phys_spec_addr >= VirtualAddressToPhysical(m_block->pc) &&
phys_spec_addr < VirtualAddressToPhysical(m_block->pc + (m_block->size * sizeof(Instruction))))
{
Log_WarningFmt("Instruction {:08X} speculatively writes to {:08X} inside block {:08X}-{:08X}. Truncating block.",
m_current_instruction_pc, phys_spec_addr, m_block->pc,
m_block->pc + (m_block->size * sizeof(Instruction)));
m_block->size = ((m_current_instruction_pc - m_block->pc) / sizeof(Instruction)) + 1;
iinfo->is_last_instruction = true;
}
}
}
void CPU::NewRec::Compiler::FlushForLoadStore(const std::optional<VirtualMemoryAddress>& address, bool store,

View file

@ -402,7 +402,7 @@ protected:
TickCount m_gte_done_cycle = 0;
const Instruction* inst = nullptr;
const CodeCache::InstructionInfo* iinfo = nullptr;
CodeCache::InstructionInfo* iinfo = nullptr;
u32 m_current_instruction_pc = 0;
bool m_current_instruction_branch_delay_slot = false;
bool m_branch_delay_slot_swapped = false;

View file

@ -1983,6 +1983,10 @@ void CPU::NewRec::AArch32Compiler::TestInterrupts(const vixl::aarch32::Register&
SwitchToFarCode(true, ne);
BackupHostState();
// Update load delay, this normally happens at the end of an instruction, but we're finishing it early.
UpdateLoadDelay();
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
// Can't use EndBlockWithException() here, because it'll use the wrong PC.

View file

@ -1957,6 +1957,10 @@ void CPU::NewRec::AArch64Compiler::TestInterrupts(const vixl::aarch64::WRegister
SwitchToFarCode(true, ne);
BackupHostState();
// Update load delay, this normally happens at the end of an instruction, but we're finishing it early.
UpdateLoadDelay();
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
// Can't use EndBlockWithException() here, because it'll use the wrong PC.

View file

@ -2212,7 +2212,12 @@ void CPU::NewRec::RISCV64Compiler::TestInterrupts(const biscuit::GPR& sr)
rvAsm->SRLIW(sr, sr, 8);
rvAsm->ANDI(sr, sr, 0xFF);
SwitchToFarCode(true, &Assembler::BEQ, sr, zero);
BackupHostState();
// Update load delay, this normally happens at the end of an instruction, but we're finishing it early.
UpdateLoadDelay();
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
// Can't use EndBlockWithException() here, because it'll use the wrong PC.

View file

@ -1935,6 +1935,10 @@ void CPU::NewRec::X64Compiler::TestInterrupts(const Xbyak::Reg32& sr)
SwitchToFarCode(true, &CodeGenerator::jnz);
BackupHostState();
// Update load delay, this normally happens at the end of an instruction, but we're finishing it early.
UpdateLoadDelay();
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
// Can't use EndBlockWithException() here, because it'll use the wrong PC.