From 200f6c451562097dd7cfd2193990a015b7bf20cf Mon Sep 17 00:00:00 2001 From: SimoneN64 Date: Mon, 6 Jan 2025 22:03:39 +0100 Subject: [PATCH] More progress on JIT constant instructions implementations --- src/backend/core/JIT.cpp | 22 +- src/backend/core/JIT.hpp | 44 ++- src/backend/core/jit/decode.cpp | 388 ++++++++++++++++++++++++++ src/backend/core/jit/helpers.hpp | 11 +- src/backend/core/jit/instructions.cpp | 69 +++++ src/backend/core/registers/Cop1.hpp | 2 - 6 files changed, 505 insertions(+), 31 deletions(-) create mode 100644 src/backend/core/jit/decode.cpp diff --git a/src/backend/core/JIT.cpp b/src/backend/core/JIT.cpp index 261fba58..8ac4f76c 100644 --- a/src/backend/core/JIT.cpp +++ b/src/backend/core/JIT.cpp @@ -23,7 +23,7 @@ void JIT::CheckCompareInterrupt() { } int JIT::Step() { - u32 instruction; + u32 instruction = 0; s64 pc = regs.pc; do { @@ -32,19 +32,23 @@ int JIT::Step() { // regs.prevDelaySlot = regs.delaySlot; // regs.delaySlot = false; - /*if (check_address_error(0b11, u64(pc))) [[unlikely]] { - regs.cop0.HandleTLBException(pc); + if (check_address_error(0b11, u64(pc))) [[unlikely]] { + /*regs.cop0.HandleTLBException(pc); regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, pc); - return 1; - }*/ + return 1;*/ + + Util::panic("[JIT]: Unhandled exception ADL due to unaligned PC virtual value! (0x{:016lX})", + static_cast(regs.pc)); + } u32 paddr = 0; if (!regs.cop0.MapVAddr(Cop0::LOAD, pc, paddr)) { /*regs.cop0.HandleTLBException(pc); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, pc); return 1;*/ - Util::panic("[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!", - static_cast(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD))); + Util::panic( + "[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address! (virtual: 0x{:016lX})", + static_cast(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)), static_cast(regs.pc)); } instruction = mem.Read(regs, paddr); @@ -56,10 +60,12 @@ int JIT::Step() { pc += 4; - // Exec(instruction); + Emit(instruction); } while (!InstrEndsBlock(instruction)); + // emit code to store the value of pc + return 1; } diff --git a/src/backend/core/JIT.hpp b/src/backend/core/JIT.hpp index e5e08ae4..685f7ecc 100644 --- a/src/backend/core/JIT.hpp +++ b/src/backend/core/JIT.hpp @@ -2,10 +2,23 @@ #include #include #include +#include namespace n64 { struct Core; +static constexpr u32 kAddressSpaceSize = 0x8000'0000; // >> 20 = 0x800 +static constexpr u32 kLowerSize = kAddressSpaceSize >> 20; // 0x800 +static constexpr u32 kUpperSize = 1 << 20; // 0x100000 +static constexpr u32 kCodeCacheSize = 32_mb; +static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4096; + +struct CodeGenerator : Xbyak::CodeGenerator { + CodeGenerator() : Xbyak::CodeGenerator{kCodeCacheSize} {} +}; + +enum BranchCondition { EQ, NE, GT, GE, LT, LE, GTU, GEU, LTU, LEU }; + struct JIT : BaseCPU { explicit JIT(ParallelRDP &); ~JIT() override = default; @@ -23,19 +36,21 @@ struct JIT : BaseCPU { [[nodiscard]] Disassembler::DisassemblyResult Disassemble(u32, u32) const override { return {}; } private: + CodeGenerator code; Registers regs; Mem mem; u64 cop2Latch{}; friend struct Cop1; + #define check_address_error(mask, vaddr) \ (((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0)) - bool ShouldServiceInterrupt() const override; + + [[nodiscard]] bool ShouldServiceInterrupt() const override; void CheckCompareInterrupt() override; std::vector Serialize() override; void Deserialize(const std::vector &) override; void Emit(u32); - void cop2Decode(u32); void special(u32); void regimm(u32); void add(u32); @@ -44,12 +59,14 @@ private: void addiu(u32); void andi(u32); void and_(u32); - void branch(bool, s64); - void branch_likely(bool, s64); - void b(u32, bool); - void blink(u32, bool); - void bl(u32, bool); - void bllink(u32, bool); + void b(u32 instr, BranchCondition, u32 reg1, u32 reg2); + void b(u32 instr, BranchCondition, u32 reg); + void blink(u32 instr, BranchCondition, u32 reg1, u32 reg2); + void blink(u32 instr, BranchCondition, u32 reg); + void bl(u32 instr, BranchCondition, u32 reg1, u32 reg2); + void bl(u32 instr, BranchCondition, u32 reg); + void bllink(u32 instr, BranchCondition, u32 reg1, u32 reg2); + void bllink(u32 instr, BranchCondition, u32 reg); void dadd(u32); void daddu(u32); void daddi(u32); @@ -79,6 +96,7 @@ private: void lbu(u32); void lb(u32); void ld(u32); + void ldc1(u32); void ldl(u32); void ldr(u32); void lh(u32); @@ -86,6 +104,7 @@ private: void ll(u32); void lld(u32); void lw(u32); + void lwc1(u32); void lwl(u32); void lwu(u32); void lwr(u32); @@ -100,6 +119,7 @@ private: void sc(u32); void scd(u32); void sd(u32); + void sdc1(u32); void sdl(u32); void sdr(u32); void sh(u32); @@ -114,6 +134,7 @@ private: void sllv(u32); void sub(u32); void subu(u32); + void swc1(u32); void sra(u32); void srav(u32); void srl(u32); @@ -123,12 +144,5 @@ private: void ori(u32); void xor_(u32); void xori(u32); - - void mtc2(u32); - void mfc2(u32); - void dmtc2(u32); - void dmfc2(u32); - void ctc2(u32); - void cfc2(u32); }; } // namespace n64 diff --git a/src/backend/core/jit/decode.cpp b/src/backend/core/jit/decode.cpp new file mode 100644 index 00000000..e0659fde --- /dev/null +++ b/src/backend/core/jit/decode.cpp @@ -0,0 +1,388 @@ +#include +#include + +namespace n64 { +void JIT::special(const u32 instr) { + // 00rr_rccc + switch (const u8 mask = instr & 0x3F) { + case SLL: + if (instr != 0) { + sll(instr); + } + break; + case SRL: + srl(instr); + break; + case SRA: + sra(instr); + break; + case SLLV: + sllv(instr); + break; + case SRLV: + srlv(instr); + break; + case SRAV: + srav(instr); + break; + case JR: + jr(instr); + break; + case JALR: + jalr(instr); + break; + case SYSCALL: + regs.cop0.FireException(ExceptionCode::Syscall, 0, regs.oldPC); + break; + case BREAK: + regs.cop0.FireException(ExceptionCode::Breakpoint, 0, regs.oldPC); + break; + case SYNC: + break; // SYNC + case MFHI: + mfhi(instr); + break; + case MTHI: + mthi(instr); + break; + case MFLO: + mflo(instr); + break; + case MTLO: + mtlo(instr); + break; + case DSLLV: + dsllv(instr); + break; + case DSRLV: + dsrlv(instr); + break; + case DSRAV: + dsrav(instr); + break; + case MULT: + mult(instr); + break; + case MULTU: + multu(instr); + break; + case DIV: + div(instr); + break; + case DIVU: + divu(instr); + break; + case DMULT: + dmult(instr); + break; + case DMULTU: + dmultu(instr); + break; + case DDIV: + ddiv(instr); + break; + case DDIVU: + ddivu(instr); + break; + case ADD: + add(instr); + break; + case ADDU: + addu(instr); + break; + case SUB: + sub(instr); + break; + case SUBU: + subu(instr); + break; + case AND: + and_(instr); + break; + case OR: + or_(instr); + break; + case XOR: + xor_(instr); + break; + case NOR: + nor(instr); + break; + case SLT: + slt(instr); + break; + case SLTU: + sltu(instr); + break; + case DADD: + dadd(instr); + break; + case DADDU: + daddu(instr); + break; + case DSUB: + dsub(instr); + break; + case DSUBU: + dsubu(instr); + break; + case TGE: + trap(regs.Read(RS(instr)) >= regs.Read(RT(instr))); + break; + case TGEU: + trap(regs.Read(RS(instr)) >= regs.Read(RT(instr))); + break; + case TLT: + trap(regs.Read(RS(instr)) < regs.Read(RT(instr))); + break; + case TLTU: + trap(regs.Read(RS(instr)) < regs.Read(RT(instr))); + break; + case TEQ: + trap(regs.Read(RS(instr)) == regs.Read(RT(instr))); + break; + case TNE: + trap(regs.Read(RS(instr)) != regs.Read(RT(instr))); + break; + case DSLL: + dsll(instr); + break; + case DSRL: + dsrl(instr); + break; + case DSRA: + dsra(instr); + break; + case DSLL32: + dsll32(instr); + break; + case DSRL32: + dsrl32(instr); + break; + case DSRA32: + dsra32(instr); + break; + default: + Util::panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", (mask >> 3) & 7, mask & 7, instr, + static_cast(regs.oldPC)); + } +} + +void JIT::regimm(const u32 instr) { + // 000r_rccc + switch (const u8 mask = instr >> 16 & 0x1F) { + case BLTZ: + b(instr, LT, RS(instr)); + break; + case BGEZ: + b(instr, GE, RS(instr)); + break; + case BLTZL: + bl(instr, LT, RS(instr)); + break; + case BGEZL: + bl(instr, GE, RS(instr)); + break; + case TGEI: + trap(regs.Read(RS(instr)) >= static_cast(static_cast(instr))); + break; + case TGEIU: + trap(regs.Read(RS(instr)) >= static_cast(static_cast(static_cast(instr)))); + break; + case TLTI: + trap(regs.Read(RS(instr)) < static_cast(static_cast(instr))); + break; + case TLTIU: + trap(regs.Read(RS(instr)) < static_cast(static_cast(static_cast(instr)))); + break; + case TEQI: + trap(regs.Read(RS(instr)) == static_cast(static_cast(instr))); + break; + case TNEI: + trap(regs.Read(RS(instr)) != static_cast(static_cast(instr))); + break; + case BLTZAL: + blink(instr, LT, RS(instr)); + break; + case BGEZAL: + blink(instr, GE, RS(instr)); + break; + case BLTZALL: + bllink(instr, LT, RS(instr)); + break; + case BGEZALL: + bllink(instr, GE, RS(instr)); + break; + default: + Util::panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})", (mask >> 3) & 3, mask & 7, instr, + static_cast(regs.oldPC)); + } +} + +void JIT::Emit(const u32 instr) { + switch (const u8 mask = instr >> 26 & 0x3f) { + case SPECIAL: + special(instr); + break; + case REGIMM: + regimm(instr); + break; + case J: + j(instr); + break; + case JAL: + jal(instr); + break; + case BEQ: + b(instr, EQ, RS(instr), RT(instr)); + break; + case BNE: + b(instr, NE, RS(instr), RT(instr)); + break; + case BLEZ: + b(instr, LE, RS(instr)); + break; + case BGTZ: + b(instr, GT, RS(instr)); + break; + case ADDI: + addi(instr); + break; + case ADDIU: + addiu(instr); + break; + case SLTI: + slti(instr); + break; + case SLTIU: + sltiu(instr); + break; + case ANDI: + andi(instr); + break; + case ORI: + ori(instr); + break; + case XORI: + xori(instr); + break; + case LUI: + lui(instr); + break; + case COP0: + regs.cop0.decode(*this, instr); + break; + case COP1: + regs.cop1.decode(*this, instr); + break; + case COP2: + break; + case BEQL: + bl(instr, EQ, RS(instr), RT(instr)); + break; + case BNEL: + bl(instr, NE, RS(instr), RT(instr)); + break; + case BLEZL: + bl(instr, LE, RS(instr)); + break; + case BGTZL: + bl(instr, GT, RS(instr)); + break; + case DADDI: + daddi(instr); + break; + case DADDIU: + daddiu(instr); + break; + case LDL: + ldl(instr); + break; + case LDR: + ldr(instr); + break; + case 0x1F: + regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC); + break; + case LB: + lb(instr); + break; + case LH: + lh(instr); + break; + case LWL: + lwl(instr); + break; + case LW: + lw(instr); + break; + case LBU: + lbu(instr); + break; + case LHU: + lhu(instr); + break; + case LWR: + lwr(instr); + break; + case LWU: + lwu(instr); + break; + case SB: + sb(instr); + break; + case SH: + sh(instr); + break; + case SWL: + swl(instr); + break; + case SW: + sw(instr); + break; + case SDL: + sdl(instr); + break; + case SDR: + sdr(instr); + break; + case SWR: + swr(instr); + break; + case CACHE: + break; // CACHE + case LL: + ll(instr); + break; + case LWC1: + lwc1(instr); + break; + case LLD: + lld(instr); + break; + case LDC1: + ldc1(instr); + break; + case LD: + ld(instr); + break; + case SC: + sc(instr); + break; + case SWC1: + swc1(instr); + break; + case SCD: + scd(instr); + break; + case SDC1: + sdc1(instr); + break; + case SD: + sd(instr); + break; + default: + Util::panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", mask, instr, static_cast(regs.oldPC)); + } +} +} // namespace n64 diff --git a/src/backend/core/jit/helpers.hpp b/src/backend/core/jit/helpers.hpp index a74f4880..819499e9 100644 --- a/src/backend/core/jit/helpers.hpp +++ b/src/backend/core/jit/helpers.hpp @@ -1,9 +1,9 @@ +#pragma once #include namespace n64 { -static inline bool SpecialEndsBlock(u32 instr) { - u8 mask = instr & 0x3F; - switch (mask) { +static bool SpecialEndsBlock(const u32 instr) { + switch (instr & 0x3F) { case JR: case JALR: case SYSCALL: @@ -20,9 +20,8 @@ static inline bool SpecialEndsBlock(u32 instr) { } } -static inline bool InstrEndsBlock(u32 instr) { - u8 mask = (instr >> 26) & 0x3f; - switch (mask) { +static bool InstrEndsBlock(const u32 instr) { + switch (instr >> 26 & 0x3f) { case SPECIAL: return SpecialEndsBlock(instr); case REGIMM: diff --git a/src/backend/core/jit/instructions.cpp b/src/backend/core/jit/instructions.cpp index 236226ea..6f38052e 100644 --- a/src/backend/core/jit/instructions.cpp +++ b/src/backend/core/jit/instructions.cpp @@ -79,6 +79,75 @@ void JIT::and_(u32 instr) { } } +void branch(Registers ®s, const bool cond, const s64 address) { + regs.delaySlot = true; + if (cond) { + regs.nextPC = address; + } +} + +void branch_likely(Registers ®s, const bool cond, const s64 address) { + if (cond) { + regs.delaySlot = true; + regs.nextPC = address; + } else { + regs.SetPC64(regs.nextPC); + } +} + +bool EvaluateCondition(Registers ®s, BranchCondition, u32, u32) { + Util::panic("[JIT]: non-constant EvaluateCondition!"); +} + +bool EvaluateConditionConstant(Registers ®s, const BranchCondition cond, const u32 reg1, const u32 reg2) { + switch (cond) { + case EQ: + return regs.Read(reg1) == regs.Read(reg2); + case NE: + return regs.Read(reg1) != regs.Read(reg2); + case LT: + return regs.Read(reg1) < regs.Read(reg2); + case LE: + return regs.Read(reg1) <= regs.Read(reg2); + case GT: + return regs.Read(reg1) > regs.Read(reg2); + case GE: + return regs.Read(reg1) >= regs.Read(reg2); + case LTU: + return regs.Read(reg1) < regs.Read(reg2); + case LEU: + return regs.Read(reg1) <= regs.Read(reg2); + case GTU: + return regs.Read(reg1) > regs.Read(reg2); + case GEU: + return regs.Read(reg1) >= regs.Read(reg2); + } +} + +void JIT::b(u32 instr, BranchCondition cond, u32 reg1, u32 reg2) { + bool isConstant = regs.IsRegConstant(reg1, reg2); + if (isConstant) { + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + const s64 address = regs.pc + offset; + branch(regs, EvaluateConditionConstant(regs, cond, reg1, reg2), address); + } +} + +void JIT::b(u32 instr, BranchCondition cond, u32 reg) {} + +void JIT::blink(u32 instr, BranchCondition cond, u32 reg1, u32 reg2) {} + +void JIT::blink(u32 instr, BranchCondition cond, u32 reg) {} + +void JIT::bl(u32 instr, BranchCondition cond, u32 reg1, u32 reg2) {} + +void JIT::bl(u32 instr, BranchCondition cond, u32 reg) {} + +void JIT::bllink(u32 instr, BranchCondition cond, u32 reg1, u32 reg2) {} + +void JIT::bllink(u32 instr, BranchCondition cond, u32 reg) {} + void JIT::dadd(u32 instr) { if (regs.IsRegConstant(RS(instr), RT(instr))) { auto rs = regs.Read(RS(instr)); diff --git a/src/backend/core/registers/Cop1.hpp b/src/backend/core/registers/Cop1.hpp index edf7e00b..071aa902 100644 --- a/src/backend/core/registers/Cop1.hpp +++ b/src/backend/core/registers/Cop1.hpp @@ -85,8 +85,6 @@ union FCR31 { } }; -enum CompConds { F, UN, EQ, UEQ, OLT, ULT, OLE, ULE, SF, NGLE, SEQ, NGL, LT, NGE, LE, NGT }; - union FloatingPointReg { struct { s32 int32;