diff --git a/src/core/cpu_newrec_compiler.cpp b/src/core/cpu_newrec_compiler.cpp index 02032e16d..f3042e0d6 100644 --- a/src/core/cpu_newrec_compiler.cpp +++ b/src/core/cpu_newrec_compiler.cpp @@ -1602,11 +1602,13 @@ void CPU::NewRec::Compiler::CompileLoadStoreTemplate(void (Compiler::*func)(Comp // constant address? std::optional addr; + std::optional 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 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& address, bool store, diff --git a/src/core/cpu_newrec_compiler.h b/src/core/cpu_newrec_compiler.h index 21924237c..894c2b625 100644 --- a/src/core/cpu_newrec_compiler.h +++ b/src/core/cpu_newrec_compiler.h @@ -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; diff --git a/src/core/cpu_newrec_compiler_aarch32.cpp b/src/core/cpu_newrec_compiler_aarch32.cpp index 2606b46ba..218bb6940 100644 --- a/src/core/cpu_newrec_compiler_aarch32.cpp +++ b/src/core/cpu_newrec_compiler_aarch32.cpp @@ -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. diff --git a/src/core/cpu_newrec_compiler_aarch64.cpp b/src/core/cpu_newrec_compiler_aarch64.cpp index 4533068e6..6725af68b 100644 --- a/src/core/cpu_newrec_compiler_aarch64.cpp +++ b/src/core/cpu_newrec_compiler_aarch64.cpp @@ -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. diff --git a/src/core/cpu_newrec_compiler_riscv64.cpp b/src/core/cpu_newrec_compiler_riscv64.cpp index c96aeeca3..bdfd2ccac 100644 --- a/src/core/cpu_newrec_compiler_riscv64.cpp +++ b/src/core/cpu_newrec_compiler_riscv64.cpp @@ -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. diff --git a/src/core/cpu_newrec_compiler_x64.cpp b/src/core/cpu_newrec_compiler_x64.cpp index ae57d6b09..fd54572aa 100644 --- a/src/core/cpu_newrec_compiler_x64.cpp +++ b/src/core/cpu_newrec_compiler_x64.cpp @@ -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.