diff --git a/src/core/cpu_recompiler_code_generator.cpp b/src/core/cpu_recompiler_code_generator.cpp index 116f8298a..3377471b0 100644 --- a/src/core/cpu_recompiler_code_generator.cpp +++ b/src/core/cpu_recompiler_code_generator.cpp @@ -1,5 +1,6 @@ #include "cpu_recompiler_code_generator.h" #include "YBaseLib/Log.h" +#include "cpu_core.h" #include "cpu_disasm.h" Log_SetChannel(CPU::Recompiler); @@ -788,8 +789,12 @@ Value CodeGenerator::NotValue(const Value& val) return res; } -void CodeGenerator::GenerateExceptionExit(Exception excode, Condition condition /* = Condition::Always */) +void CodeGenerator::GenerateExceptionExit(const CodeBlockInstruction& cbi, Exception excode, + Condition condition /* = Condition::Always */) { + const Value epc = Value::FromConstantU32(cbi.pc); + const Value ri_bits = Value::FromConstantU32(Thunks::MakeRaiseExceptionInfo(excode, cbi)); + if (condition == Condition::Always) { // no need to use far code if we're always raising the exception @@ -797,8 +802,7 @@ void CodeGenerator::GenerateExceptionExit(Exception excode, Condition condition m_register_cache.FlushAllGuestRegisters(true, true); m_register_cache.FlushLoadDelay(true); - EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(), - Value::FromConstantU8(static_cast(excode))); + EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(), epc, ri_bits); return; } @@ -810,8 +814,7 @@ void CodeGenerator::GenerateExceptionExit(Exception excode, Condition condition EmitBranch(GetCurrentFarCodePointer()); SwitchToFarCode(); - EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(), - Value::FromConstantU8(static_cast(excode))); + EmitFunctionCall(nullptr, &Thunks::RaiseException, m_register_cache.GetCPUPtr(), epc, ri_bits); EmitExceptionExit(); SwitchToNearCode(); @@ -842,7 +845,7 @@ void CodeGenerator::BlockEpilogue() if (m_register_cache.HasLoadDelay()) m_register_cache.WriteLoadDelayToCPU(true); - AddPendingCycles(); + AddPendingCycles(true); } void CodeGenerator::InstructionPrologue(const CodeBlockInstruction& cbi, TickCount cycles, @@ -881,7 +884,7 @@ void CodeGenerator::InstructionPrologue(const CodeBlockInstruction& cbi, TickCou m_register_cache.WriteGuestRegister(Reg::pc, Value::FromConstantU32(cbi.pc + 4)); } - if (!CanInstructionTrap(cbi.instruction, m_block->key.user_mode) && !force_sync) + if (!force_sync) { // Defer updates for non-faulting instructions. m_delayed_cycles_add += cycles; @@ -895,11 +898,9 @@ void CodeGenerator::InstructionPrologue(const CodeBlockInstruction& cbi, TickCou m_current_instruction_in_branch_delay_slot_dirty = true; } - // Sync current instruction PC - EmitStoreCPUStructField(offsetof(Core, m_current_instruction_pc), Value::FromConstantU32(cbi.pc)); - m_delayed_cycles_add += cycles; - AddPendingCycles(); + SetCurrentInstructionPC(cbi); + AddPendingCycles(true); } void CodeGenerator::InstructionEpilogue(const CodeBlockInstruction& cbi) @@ -925,13 +926,20 @@ void CodeGenerator::InstructionEpilogue(const CodeBlockInstruction& cbi) } } -void CodeGenerator::AddPendingCycles() +void CodeGenerator::AddPendingCycles(bool commit) { if (m_delayed_cycles_add == 0) return; EmitAddCPUStructField(offsetof(Core, m_pending_ticks), Value::FromConstantU32(m_delayed_cycles_add)); - m_delayed_cycles_add = 0; + + if (commit) + m_delayed_cycles_add = 0; +} + +void CodeGenerator::SetCurrentInstructionPC(const CodeBlockInstruction& cbi) +{ + EmitStoreCPUStructField(offsetof(Core, m_current_instruction_pc), Value::FromConstantU32(cbi.pc)); } bool CodeGenerator::Compile_Fallback(const CodeBlockInstruction& cbi) @@ -1107,18 +1115,18 @@ bool CodeGenerator::Compile_Load(const CodeBlockInstruction& cbi) { case InstructionOp::lb: case InstructionOp::lbu: - result = EmitLoadGuestMemory(address, RegSize_8); + result = EmitLoadGuestMemory(cbi, address, RegSize_8); ConvertValueSizeInPlace(&result, RegSize_32, (cbi.instruction.op == InstructionOp::lb)); break; case InstructionOp::lh: case InstructionOp::lhu: - result = EmitLoadGuestMemory(address, RegSize_16); + result = EmitLoadGuestMemory(cbi, address, RegSize_16); ConvertValueSizeInPlace(&result, RegSize_32, (cbi.instruction.op == InstructionOp::lh)); break; case InstructionOp::lw: - result = EmitLoadGuestMemory(address, RegSize_32); + result = EmitLoadGuestMemory(cbi, address, RegSize_32); break; default: @@ -1145,15 +1153,15 @@ bool CodeGenerator::Compile_Store(const CodeBlockInstruction& cbi) switch (cbi.instruction.op) { case InstructionOp::sb: - EmitStoreGuestMemory(address, value.ViewAsSize(RegSize_8)); + EmitStoreGuestMemory(cbi, address, value.ViewAsSize(RegSize_8)); break; case InstructionOp::sh: - EmitStoreGuestMemory(address, value.ViewAsSize(RegSize_16)); + EmitStoreGuestMemory(cbi, address, value.ViewAsSize(RegSize_16)); break; case InstructionOp::sw: - EmitStoreGuestMemory(address, value); + EmitStoreGuestMemory(cbi, address, value); break; default: @@ -1234,7 +1242,7 @@ bool CodeGenerator::Compile_Add(const CodeBlockInstruction& cbi) Value result = AddValues(lhs, rhs, check_overflow); if (check_overflow) - GenerateExceptionExit(Exception::Ov, Condition::Overflow); + GenerateExceptionExit(cbi, Exception::Ov, Condition::Overflow); m_register_cache.WriteGuestRegister(dest, std::move(result)); @@ -1254,7 +1262,7 @@ bool CodeGenerator::Compile_Subtract(const CodeBlockInstruction& cbi) Value result = SubValues(lhs, rhs, check_overflow); if (check_overflow) - GenerateExceptionExit(Exception::Ov, Condition::Overflow); + GenerateExceptionExit(cbi, Exception::Ov, Condition::Overflow); m_register_cache.WriteGuestRegister(cbi.instruction.r.rd, std::move(result)); @@ -1440,7 +1448,7 @@ bool CodeGenerator::Compile_Branch(const CodeBlockInstruction& cbi) { const Exception excode = (cbi.instruction.r.funct == InstructionFunct::syscall) ? Exception::Syscall : Exception::BP; - GenerateExceptionExit(excode); + GenerateExceptionExit(cbi, excode); } else { @@ -1785,13 +1793,13 @@ bool CodeGenerator::Compile_cop2(const CodeBlockInstruction& cbi) Value::FromConstantU32(cbi.instruction.i.imm_sext32()), false); if (cbi.instruction.op == InstructionOp::lwc2) { - Value value = EmitLoadGuestMemory(address, RegSize_32); + Value value = EmitLoadGuestMemory(cbi, address, RegSize_32); DoGTERegisterWrite(reg, value); } else { Value value = DoGTERegisterRead(reg); - EmitStoreGuestMemory(address, value); + EmitStoreGuestMemory(cbi, address, value); } InstructionEpilogue(cbi); diff --git a/src/core/cpu_recompiler_code_generator.h b/src/core/cpu_recompiler_code_generator.h index fda757c2f..84355a2f7 100644 --- a/src/core/cpu_recompiler_code_generator.h +++ b/src/core/cpu_recompiler_code_generator.h @@ -75,8 +75,8 @@ public: void EmitAddCPUStructField(u32 offset, const Value& value); // Automatically generates an exception handler. - Value EmitLoadGuestMemory(const Value& address, RegSize size); - void EmitStoreGuestMemory(const Value& address, const Value& value); + Value EmitLoadGuestMemory(const CodeBlockInstruction& cbi, const Value& address, RegSize size); + void EmitStoreGuestMemory(const CodeBlockInstruction& cbi, const Value& address, const Value& value); // Unconditional branch to pointer. May allocate a scratch register. void EmitBranch(const void* address, bool allow_scratch = true); @@ -148,7 +148,8 @@ public: Value NotValue(const Value& val); // Raising exception if condition is true. - void GenerateExceptionExit(Exception excode, Condition condition = Condition::Always); + void GenerateExceptionExit(const CodeBlockInstruction& cbi, Exception excode, + Condition condition = Condition::Always); private: // Host register setup @@ -173,7 +174,8 @@ private: void BlockEpilogue(); void InstructionPrologue(const CodeBlockInstruction& cbi, TickCount cycles, bool force_sync = false); void InstructionEpilogue(const CodeBlockInstruction& cbi); - void AddPendingCycles(); + void SetCurrentInstructionPC(const CodeBlockInstruction& cbi); + void AddPendingCycles(bool commit); Value DoGTERegisterRead(u32 index); void DoGTERegisterWrite(u32 index, const Value& value); diff --git a/src/core/cpu_recompiler_code_generator_aarch64.cpp b/src/core/cpu_recompiler_code_generator_aarch64.cpp index 18fbde2d3..1f5b74518 100644 --- a/src/core/cpu_recompiler_code_generator_aarch64.cpp +++ b/src/core/cpu_recompiler_code_generator_aarch64.cpp @@ -1,6 +1,7 @@ #include "YBaseLib/Log.h" #include "cpu_recompiler_code_generator.h" #include "cpu_recompiler_thunks.h" +#include "cpu_core.h" Log_SetChannel(CPU::Recompiler); namespace a64 = vixl::aarch64; @@ -1209,8 +1210,11 @@ void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) } } -Value CodeGenerator::EmitLoadGuestMemory(const Value& address, RegSize size) +Value CodeGenerator::EmitLoadGuestMemory(const CodeBlockInstruction& cbi, const Value& address, RegSize size) { + const Value pc = Value::FromConstantU32(cbi.pc); + AddPendingCycles(true); + // We need to use the full 64 bits here since we test the sign bit result. Value result = m_register_cache.AllocateScratch(RegSize_64); @@ -1218,15 +1222,15 @@ Value CodeGenerator::EmitLoadGuestMemory(const Value& address, RegSize size) switch (size) { case RegSize_8: - EmitFunctionCall(&result, &Thunks::ReadMemoryByte, m_register_cache.GetCPUPtr(), address); + EmitFunctionCall(&result, &Thunks::ReadMemoryByte, m_register_cache.GetCPUPtr(), pc, address); break; case RegSize_16: - EmitFunctionCall(&result, &Thunks::ReadMemoryHalfWord, m_register_cache.GetCPUPtr(), address); + EmitFunctionCall(&result, &Thunks::ReadMemoryHalfWord, m_register_cache.GetCPUPtr(), pc, address); break; case RegSize_32: - EmitFunctionCall(&result, &Thunks::ReadMemoryWord, m_register_cache.GetCPUPtr(), address); + EmitFunctionCall(&result, &Thunks::ReadMemoryWord, m_register_cache.GetCPUPtr(), pc, address); break; default: @@ -1271,22 +1275,25 @@ Value CodeGenerator::EmitLoadGuestMemory(const Value& address, RegSize size) return result; } -void CodeGenerator::EmitStoreGuestMemory(const Value& address, const Value& value) +void CodeGenerator::EmitStoreGuestMemory(const CodeBlockInstruction& cbi, const Value& address, const Value& value) { + const Value pc = Value::FromConstantU32(cbi.pc); + AddPendingCycles(true); + Value result = m_register_cache.AllocateScratch(RegSize_8); switch (value.size) { case RegSize_8: - EmitFunctionCall(&result, &Thunks::WriteMemoryByte, m_register_cache.GetCPUPtr(), address, value); + EmitFunctionCall(&result, &Thunks::WriteMemoryByte, m_register_cache.GetCPUPtr(), pc, address, value); break; case RegSize_16: - EmitFunctionCall(&result, &Thunks::WriteMemoryHalfWord, m_register_cache.GetCPUPtr(), address, value); + EmitFunctionCall(&result, &Thunks::WriteMemoryHalfWord, m_register_cache.GetCPUPtr(), pc, address, value); break; case RegSize_32: - EmitFunctionCall(&result, &Thunks::WriteMemoryWord, m_register_cache.GetCPUPtr(), address, value); + EmitFunctionCall(&result, &Thunks::WriteMemoryWord, m_register_cache.GetCPUPtr(), pc, address, value); break; default: diff --git a/src/core/cpu_recompiler_code_generator_generic.cpp b/src/core/cpu_recompiler_code_generator_generic.cpp index c4927c018..22ac526df 100644 --- a/src/core/cpu_recompiler_code_generator_generic.cpp +++ b/src/core/cpu_recompiler_code_generator_generic.cpp @@ -1,3 +1,4 @@ +#include "cpu_core.h" #include "cpu_recompiler_code_generator.h" namespace CPU::Recompiler { diff --git a/src/core/cpu_recompiler_code_generator_x64.cpp b/src/core/cpu_recompiler_code_generator_x64.cpp index 249713410..244327fe6 100644 --- a/src/core/cpu_recompiler_code_generator_x64.cpp +++ b/src/core/cpu_recompiler_code_generator_x64.cpp @@ -1,6 +1,7 @@ #include "YBaseLib/Log.h" #include "cpu_recompiler_code_generator.h" #include "cpu_recompiler_thunks.h" +#include "cpu_core.h" Log_SetChannel(CPU::Recompiler); namespace CPU::Recompiler { @@ -201,6 +202,8 @@ void CodeGenerator::EmitEndBlock() void CodeGenerator::EmitExceptionExit() { + AddPendingCycles(false); + // toss away our PC value since we're jumping to the exception handler m_register_cache.InvalidateGuestRegister(Reg::pc); @@ -1632,8 +1635,11 @@ void CodeGenerator::EmitAddCPUStructField(u32 offset, const Value& value) } } -Value CodeGenerator::EmitLoadGuestMemory(const Value& address, RegSize size) +Value CodeGenerator::EmitLoadGuestMemory(const CodeBlockInstruction& cbi, const Value& address, RegSize size) { + const Value pc = Value::FromConstantU32(cbi.pc); + AddPendingCycles(true); + // We need to use the full 64 bits here since we test the sign bit result. Value result = m_register_cache.AllocateScratch(RegSize_64); @@ -1641,15 +1647,15 @@ Value CodeGenerator::EmitLoadGuestMemory(const Value& address, RegSize size) switch (size) { case RegSize_8: - EmitFunctionCall(&result, &Thunks::ReadMemoryByte, m_register_cache.GetCPUPtr(), address); + EmitFunctionCall(&result, &Thunks::ReadMemoryByte, m_register_cache.GetCPUPtr(), pc, address); break; case RegSize_16: - EmitFunctionCall(&result, &Thunks::ReadMemoryHalfWord, m_register_cache.GetCPUPtr(), address); + EmitFunctionCall(&result, &Thunks::ReadMemoryHalfWord, m_register_cache.GetCPUPtr(), pc, address); break; case RegSize_32: - EmitFunctionCall(&result, &Thunks::ReadMemoryWord, m_register_cache.GetCPUPtr(), address); + EmitFunctionCall(&result, &Thunks::ReadMemoryWord, m_register_cache.GetCPUPtr(), pc, address); break; default: @@ -1692,22 +1698,25 @@ Value CodeGenerator::EmitLoadGuestMemory(const Value& address, RegSize size) return result; } -void CodeGenerator::EmitStoreGuestMemory(const Value& address, const Value& value) +void CodeGenerator::EmitStoreGuestMemory(const CodeBlockInstruction& cbi, const Value& address, const Value& value) { + const Value pc = Value::FromConstantU32(cbi.pc); + AddPendingCycles(true); + Value result = m_register_cache.AllocateScratch(RegSize_8); switch (value.size) { case RegSize_8: - EmitFunctionCall(&result, &Thunks::WriteMemoryByte, m_register_cache.GetCPUPtr(), address, value); + EmitFunctionCall(&result, &Thunks::WriteMemoryByte, m_register_cache.GetCPUPtr(), pc, address, value); break; case RegSize_16: - EmitFunctionCall(&result, &Thunks::WriteMemoryHalfWord, m_register_cache.GetCPUPtr(), address, value); + EmitFunctionCall(&result, &Thunks::WriteMemoryHalfWord, m_register_cache.GetCPUPtr(), pc, address, value); break; case RegSize_32: - EmitFunctionCall(&result, &Thunks::WriteMemoryWord, m_register_cache.GetCPUPtr(), address, value); + EmitFunctionCall(&result, &Thunks::WriteMemoryWord, m_register_cache.GetCPUPtr(), pc, address, value); break; default: diff --git a/src/core/cpu_recompiler_thunks.cpp b/src/core/cpu_recompiler_thunks.cpp index 9c648ae19..c1dcbc7f5 100644 --- a/src/core/cpu_recompiler_thunks.cpp +++ b/src/core/cpu_recompiler_thunks.cpp @@ -1,11 +1,24 @@ #include "cpu_recompiler_thunks.h" +#include "cpu_code_cache.h" +#include "cpu_core.h" namespace CPU::Recompiler { +u32 Thunks::MakeRaiseExceptionInfo(Exception excode, const CodeBlockInstruction& cbi) +{ + RaiseExceptionInfo ri = {}; + ri.excode = static_cast(excode); + ri.BD = cbi.is_branch_delay_slot; + ri.CE = cbi.instruction.cop.cop_n; + return ri.bits; +} + // TODO: Port thunks to "ASM routines", i.e. code in the jit buffer. -u64 Thunks::ReadMemoryByte(Core* cpu, u32 address) +u64 Thunks::ReadMemoryByte(Core* cpu, u32 pc, u32 address) { + cpu->m_current_instruction_pc = pc; + u32 temp = 0; const TickCount cycles = cpu->DoMemoryAccess(address, temp); if (cycles < 0) @@ -18,8 +31,10 @@ u64 Thunks::ReadMemoryByte(Core* cpu, u32 address) return ZeroExtend64(temp); } -u64 Thunks::ReadMemoryHalfWord(Core* cpu, u32 address) +u64 Thunks::ReadMemoryHalfWord(Core* cpu, u32 pc, u32 address) { + cpu->m_current_instruction_pc = pc; + if (!cpu->DoAlignmentCheck(address)) return UINT64_C(0xFFFFFFFFFFFFFFFF); @@ -35,8 +50,10 @@ u64 Thunks::ReadMemoryHalfWord(Core* cpu, u32 address) return ZeroExtend64(temp); } -u64 Thunks::ReadMemoryWord(Core* cpu, u32 address) +u64 Thunks::ReadMemoryWord(Core* cpu, u32 pc, u32 address) { + cpu->m_current_instruction_pc = pc; + if (!cpu->DoAlignmentCheck(address)) return UINT64_C(0xFFFFFFFFFFFFFFFF); @@ -52,19 +69,57 @@ u64 Thunks::ReadMemoryWord(Core* cpu, u32 address) return ZeroExtend64(temp); } -bool Thunks::WriteMemoryByte(Core* cpu, u32 address, u8 value) +bool Thunks::WriteMemoryByte(Core* cpu, u32 pc, u32 address, u8 value) { - return cpu->WriteMemoryByte(address, value); + cpu->m_current_instruction_pc = pc; + + u32 temp = ZeroExtend32(value); + const TickCount cycles = cpu->DoMemoryAccess(address, temp); + if (cycles < 0) + { + cpu->RaiseException(Exception::DBE); + return false; + } + + DebugAssert(cycles == 0); + return true; } -bool Thunks::WriteMemoryHalfWord(Core* cpu, u32 address, u16 value) +bool Thunks::WriteMemoryHalfWord(Core* cpu, u32 pc, u32 address, u16 value) { - return cpu->WriteMemoryHalfWord(address, value); + cpu->m_current_instruction_pc = pc; + + if (!cpu->DoAlignmentCheck(address)) + return false; + + u32 temp = ZeroExtend32(value); + const TickCount cycles = cpu->DoMemoryAccess(address, temp); + if (cycles < 0) + { + cpu->RaiseException(Exception::DBE); + return false; + } + + DebugAssert(cycles == 0); + return true; } -bool Thunks::WriteMemoryWord(Core* cpu, u32 address, u32 value) +bool Thunks::WriteMemoryWord(Core* cpu, u32 pc, u32 address, u32 value) { - return cpu->WriteMemoryWord(address, value); + cpu->m_current_instruction_pc = pc; + + if (!cpu->DoAlignmentCheck(address)) + return false; + + const TickCount cycles = cpu->DoMemoryAccess(address, value); + if (cycles < 0) + { + cpu->RaiseException(Exception::DBE); + return false; + } + + DebugAssert(cycles == 0); + return true; } bool Thunks::InterpretInstruction(Core* cpu) @@ -78,9 +133,11 @@ void Thunks::UpdateLoadDelay(Core* cpu) cpu->UpdateLoadDelay(); } -void Thunks::RaiseException(Core* cpu, u8 excode) +void Thunks::RaiseException(Core* cpu, u32 epc, u32 ri_bits) { - cpu->RaiseException(static_cast(excode)); + const RaiseExceptionInfo ri{ri_bits}; + cpu->RaiseException(static_cast(ri.excode), epc, ri.BD, cpu->m_current_instruction_was_branch_taken, + ri.CE); } void Thunks::RaiseAddressException(Core* cpu, u32 address, bool store, bool branch) diff --git a/src/core/cpu_recompiler_thunks.h b/src/core/cpu_recompiler_thunks.h index 1c657618c..28c4db1d1 100644 --- a/src/core/cpu_recompiler_thunks.h +++ b/src/core/cpu_recompiler_thunks.h @@ -1,27 +1,48 @@ #pragma once -#include "common/jit_code_buffer.h" -#include "cpu_core.h" -#include +#include "cpu_types.h" -namespace CPU::Recompiler { +class JitCodeBuffer; + +namespace CPU { + +struct CodeBlockInstruction; + +class Core; + +namespace Recompiler { class Thunks { public: + union RaiseExceptionInfo + { + u32 bits; + + struct + { + u8 excode; + bool BD; + u8 CE; + u8 unused; + }; + }; + + static u32 MakeRaiseExceptionInfo(Exception excode, const CodeBlockInstruction& cbi); + ////////////////////////////////////////////////////////////////////////// // Trampolines for calling back from the JIT // Needed because we can't cast member functions to void*... // TODO: Abuse carry flag or something else for exception ////////////////////////////////////////////////////////////////////////// - static u64 ReadMemoryByte(Core* cpu, u32 address); - static u64 ReadMemoryHalfWord(Core* cpu, u32 address); - static u64 ReadMemoryWord(Core* cpu, u32 address); - static bool WriteMemoryByte(Core* cpu, u32 address, u8 value); - static bool WriteMemoryHalfWord(Core* cpu, u32 address, u16 value); - static bool WriteMemoryWord(Core* cpu, u32 address, u32 value); + static u64 ReadMemoryByte(Core* cpu, u32 pc, u32 address); + static u64 ReadMemoryHalfWord(Core* cpu, u32 pc, u32 address); + static u64 ReadMemoryWord(Core* cpu, u32 pc, u32 address); + static bool WriteMemoryByte(Core* cpu, u32 pc, u32 address, u8 value); + static bool WriteMemoryHalfWord(Core* cpu, u32 pc, u32 address, u16 value); + static bool WriteMemoryWord(Core* cpu, u32 pc, u32 address, u32 value); static bool InterpretInstruction(Core* cpu); static void UpdateLoadDelay(Core* cpu); - static void RaiseException(Core* cpu, u8 excode); + static void RaiseException(Core* cpu, u32 epc, u32 ri_bits); static void RaiseAddressException(Core* cpu, u32 address, bool store, bool branch); static void ExecuteGTEInstruction(Core* cpu, u32 instruction_bits); static u32 ReadGTERegister(Core* cpu, u32 reg); @@ -41,4 +62,6 @@ public: void Generate(JitCodeBuffer* code_buffer); }; -} // namespace CPU_X86::Recompiler +} // namespace Recompiler + +} // namespace CPU \ No newline at end of file