From b6426f2c8af85c9d8d6a8915cabd4674ce2ea5fe Mon Sep 17 00:00:00 2001 From: irisz64 Date: Fri, 19 Dec 2025 14:02:31 +0100 Subject: [PATCH] eventually figure out why xbyak complains about certains movs --- src/backend/core/JIT.cpp | 125 +++++++++++--------------- src/backend/core/JIT.hpp | 22 +++-- src/backend/core/Mem.cpp | 2 +- src/backend/core/Mem.hpp | 3 +- src/backend/core/jit/instructions.cpp | 42 +++++---- 5 files changed, 99 insertions(+), 95 deletions(-) diff --git a/src/backend/core/JIT.cpp b/src/backend/core/JIT.cpp index 33706440..86331a74 100644 --- a/src/backend/core/JIT.cpp +++ b/src/backend/core/JIT.cpp @@ -5,6 +5,7 @@ namespace n64 { #ifndef __aarch64__ JIT::JIT(Mem& mem, Registers& regs) : regs(regs), mem(mem) { regs.SetJIT(this); + mem.SetJIT(this); blockCache.resize(kUpperSize); if (cs_open(CS_ARCH_MIPS, static_cast(CS_MODE_MIPS64 | CS_MODE_BIG_ENDIAN), &disassemblerMips) != CS_ERR_OK) { @@ -39,20 +40,20 @@ void JIT::InvalidateBlock(const u32 paddr) { blockCache[index] = {}; } -u32 JIT::FetchInstruction() { +std::optional JIT::FetchInstruction(s64 vaddr) { u32 paddr = 0; - if (check_address_error(0b11, u64(blockPC))) [[unlikely]] { + if (check_address_error(0b11, vaddr)) [[unlikely]] { /*regs.cop0.HandleTLBException(blockPC); regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC); return 1;*/ Util::Error::GetInstance().Throw( {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {}, "[JIT]: Unhandled exception ADL due to unaligned PC virtual value!"); - return 0; + return std::nullopt; } - if (!regs.cop0.MapVAddr(Cop0::LOAD, blockPC, paddr)) { + if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) { /*regs.cop0.HandleTLBException(blockPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC); return 1;*/ @@ -60,7 +61,7 @@ u32 JIT::FetchInstruction() { {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {}, "[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!", static_cast(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD))); - return 0; + return std::nullopt; } const u32 instr = Core::GetMem().Read(paddr); @@ -142,7 +143,6 @@ u32 JIT::Step() { code.setProtectModeRW(); u32 instructionsInBlock = 0; - u32 instruction = 0; bool instrEndsBlock = false; @@ -152,87 +152,70 @@ u32 JIT::Step() { cs_insn *insn; info("\tMIPS code (guest PC = 0x{:016X}):", static_cast(blockPC)); - while (!instrEndsBlock) { - code.mov(code.SCR1, REG(byte, delaySlot)); - code.mov(REG(byte, prevDelaySlot), code.SCR1); - code.mov(REG(byte, delaySlot), 0); + + emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this); - // CheckCompareInterrupt(); + while (true) { paddr = 0; - if (check_address_error(0b11, u64(blockPC))) [[unlikely]] { - /*regs.cop0.HandleTLBException(blockPC); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC); - return 1;*/ - Util::Error::GetInstance().Throw( - {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {}, - "[JIT]: Unhandled exception ADL due to unaligned PC virtual value!"); - return 0; - } + auto instruction = FetchInstruction(blockPC); - if (!regs.cop0.MapVAddr(Cop0::LOAD, blockPC, paddr)) { - /*regs.cop0.HandleTLBException(blockPC); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC); - return 1;*/ - Util::Error::GetInstance().Throw( - {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {}, - "[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!", - static_cast(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD))); + if(!instruction) return 0; - } - instruction = Core::GetMem().Read(paddr); instructionsInBlock++; blockOldPC = blockPC; blockPC = blockNextPC; blockNextPC += 4; - info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instruction).full); - if((instrEndsBlock = InstrEndsBlock(instruction))) - continue; + if(InstrEndsBlock(instruction.value())) { + const auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot + if(!delay_instruction) + return 0; - /*if(ShouldServiceInterrupt()) { - regs.cop0.FireException(ExceptionCode::Interrupt, 0, blockPC); - return 1; - }*/ + if(InstrEndsBlock(delay_instruction.value())) { + Util::Error::GetInstance().Throw( + {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, + blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!"); + return 0; + } - Emit(instruction); + instructionsInBlock++; + + blockOldPC = blockPC; + blockPC = blockNextPC; + blockNextPC += 4; + + Emit(delay_instruction.value()); + Emit(instruction.value()); + + if(!branch_taken) { + Xbyak::Label runtime_branch_taken; + code.mov(code.SCR1, JIT_VAR(byte, branch_taken)); + code.cmp(code.SCR1, 0); + code.jne(runtime_branch_taken); + code.mov(code.SCR1, blockOldPC); + code.mov(REG(qword, oldPC), code.SCR1); + code.mov(code.SCR1, blockPC); + code.mov(REG(qword, pc), code.SCR1); + code.mov(code.SCR1, blockNextPC); + code.mov(REG(qword, nextPC), code.SCR1); + code.L(runtime_branch_taken); + } + + if(branch_taken) branch_taken = false; + + emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this); + + break; + } + + Emit(instruction.value()); + + emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this); } - instructionsInBlock++; - const u32 delay_instruction = FetchInstruction(); - - instrEndsBlock = InstrEndsBlock(delay_instruction); - if(instrEndsBlock) { - Util::Error::GetInstance().Throw( - {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, - blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!"); - return 0; - } - - blockOldPC = blockPC; - blockPC = blockNextPC; - blockNextPC += 4; - - Emit(delay_instruction); - - Emit(instruction); - - Xbyak::Label clearDelaySlot; - - code.mov(code.SCR1, REG(byte, delaySlot)); - code.cmp(code.SCR1, 0); - code.jne(clearDelaySlot); - code.mov(code.SCR1, blockOldPC); - code.mov(REG(qword, oldPC), code.SCR1); - code.mov(code.SCR1, blockPC); - code.mov(REG(qword, pc), code.SCR1); - code.mov(code.SCR1, blockNextPC); - code.mov(REG(qword, nextPC), code.SCR1); - code.L(clearDelaySlot); - code.mov(REG(byte, delaySlot), 0); - code.mov(code.rax, instructionsInBlock); code.pop(code.rbp); code.add(code.rsp, 8); diff --git a/src/backend/core/JIT.hpp b/src/backend/core/JIT.hpp index 87aa7030..2cd06375 100644 --- a/src/backend/core/JIT.hpp +++ b/src/backend/core/JIT.hpp @@ -16,7 +16,13 @@ static constexpr u32 kUpperSize = kAddressSpaceSize >> kUpperShift; // 0x800000 static constexpr u32 kLowerSize = 0x100; // 0x80 static constexpr u32 kCodeCacheSize = 32_mb; static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb; +#if 1 +#define REG(acc, x) code.acc[reinterpret_cast(®s.x)] +#define JIT_VAR(acc, x) code.acc[reinterpret_cast(&x)] +#else #define REG(acc, x) code.acc[code.rbp + (reinterpret_cast(®s.x) - reinterpret_cast(this))] +#define JIT_VAR(acc, x) code.acc[code.rbp + (reinterpret_cast(&x) - reinterpret_cast(this))] +#endif #ifdef __aarch64__ struct JIT : BaseCPU {}; @@ -34,6 +40,11 @@ struct JIT final : BaseCPU { void DumpBlockCacheToDisk() const; + void AdvanceDelaySlot() { + regs.prevDelaySlot = regs.delaySlot; + regs.delaySlot = false; + } + void InvalidateBlock(u32); private: Registers& regs; @@ -46,18 +57,19 @@ private: using BlockFn = int (*)(); std::vector> blockCache; Xbyak::Label branch_likely_not_taken; + bool branch_taken; csh disassemblerMips{}, disassemblerX86{}; template Xbyak::Address GPR(const size_t index) const { if constexpr (sizeof(T) == 1) { - return code.byte[reinterpret_cast(®s.gpr[index])]; + return REG(byte, gpr[index]); } else if constexpr (sizeof(T) == 2) { - return code.word[reinterpret_cast(®s.gpr[index])]; + return REG(word, gpr[index]); } else if constexpr (sizeof(T) == 4) { - return code.dword[reinterpret_cast(®s.gpr[index])]; + return REG(dword, gpr[index]); } else if constexpr (sizeof(T) == 8) { - return code.qword[reinterpret_cast(®s.gpr[index])]; + return REG(qword, gpr[index]); } Util::Error::GetInstance().Throw( @@ -109,7 +121,7 @@ private: [[nodiscard]] bool ShouldServiceInterrupt() const; void CheckCompareInterrupt() const; - u32 FetchInstruction(); + std::optional FetchInstruction(s64); void Emit(Instruction); void special(Instruction); diff --git a/src/backend/core/Mem.cpp b/src/backend/core/Mem.cpp index 57928af7..72ee2758 100644 --- a/src/backend/core/Mem.cpp +++ b/src/backend/core/Mem.cpp @@ -6,7 +6,7 @@ #include namespace n64 { -Mem::Mem(JIT *jit) : flash(saveData), jit(jit) { +Mem::Mem() : flash(saveData) { rom.cart.resize(CART_SIZE); std::ranges::fill(rom.cart, 0); } diff --git a/src/backend/core/Mem.hpp b/src/backend/core/Mem.hpp index 938eb004..a65059ae 100644 --- a/src/backend/core/Mem.hpp +++ b/src/backend/core/Mem.hpp @@ -78,10 +78,11 @@ struct JIT; struct Mem { ~Mem() = default; - Mem(JIT * = nullptr); + Mem(); void Reset(); void LoadSRAM(SaveType, fs::path); void LoadROM(bool, const std::string &); + void SetJIT(JIT* jit) { this->jit = jit; } [[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); } [[nodiscard]] auto GetRDRAM() -> std::vector & { return mmio.rdp.rdram; } diff --git a/src/backend/core/jit/instructions.cpp b/src/backend/core/jit/instructions.cpp index 1fe341e9..356f2d39 100644 --- a/src/backend/core/jit/instructions.cpp +++ b/src/backend/core/jit/instructions.cpp @@ -194,15 +194,15 @@ void JIT::blfc1(const Instruction instr) { void JIT::BranchNotTaken() {} void JIT::BranchTaken(const s64 offs) { - code.mov(code.SCR1, REG(qword, pc)); - code.add(code.SCR1, offs); - SetPC64(code.SCR1); + code.mov(code.SCR2, REG(qword, pc)); + code.add(code.SCR2, offs); + SetPC64(code.SCR2); } void JIT::BranchTaken(const Xbyak::Reg64 &offs) { - code.mov(code.SCR1, REG(qword, pc)); - code.add(code.SCR1, offs); - SetPC64(code.SCR1); + code.mov(code.SCR2, REG(qword, pc)); + code.add(code.SCR2, offs); + SetPC64(code.SCR2); } void JIT::BranchAbsTaken(const s64 addr) { @@ -217,15 +217,7 @@ void JIT::branch_constant(const bool cond, const s64 offset) { if(cond) { regs.delaySlot = true; BranchTaken(offset); - } -} - -void JIT::branch_likely_constant(const bool cond, const s64 offset) { - if(cond) { - regs.delaySlot = true; - BranchTaken(offset); - } else { - SetPC64(blockNextPC); + branch_taken = true; } } @@ -233,6 +225,17 @@ void JIT::branch_abs_constant(const bool cond, const s64 address) { if(cond) { regs.delaySlot = true; BranchAbsTaken(address); + branch_taken = true; + } +} + +void JIT::branch_likely_constant(const bool cond, const s64 offset) { + if(cond) { + regs.delaySlot = true; + BranchTaken(offset); + branch_taken = true; + } else { + SetPC64(blockNextPC); } } @@ -242,6 +245,7 @@ void JIT::branch_abs_constant(const bool cond, const s64 address) { code.jmp(not_taken); \ code.L(taken); \ BranchTaken(offs); \ + code.mov(JIT_VAR(byte, branch_taken), 1); \ code.L(not_taken); \ } while(0) @@ -251,17 +255,21 @@ void JIT::branch_abs_constant(const bool cond, const s64 address) { code.jmp(not_taken); \ code.L(taken); \ BranchAbsTaken(addr); \ + code.mov(JIT_VAR(byte, branch_taken), 1); \ code.L(not_taken); \ } while(0) #define branch_likely(offs, cond) do { \ - Xbyak::Label taken, not_taken; \ + Xbyak::Label taken, not_taken, end; \ code.j## cond(taken); \ code.jmp(not_taken); \ code.L(taken); \ BranchTaken(offs); \ + code.mov(JIT_VAR(byte, branch_taken), 1); \ + code.jmp(end); \ code.L(not_taken); \ SetPC64(blockNextPC); \ + code.L(end); \ } while(0) void JIT::bltz(const Instruction instr) { @@ -906,7 +914,7 @@ void JIT::lb(const Instruction instr) { if (u32 paddr = 0; !regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { // regs.cop0.HandleTLBException(address); // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LB (pc: 0x{:016X})!", blockPC); + panic("[JIT]: Unhandled TLBL exception in LB (pc: 0x{:016X})!", static_cast(blockPC)); } else { code.mov(code.ARG2, paddr); emitMemberFunctionCall(&Mem::Read, &mem);