More progress on JIT constant instructions implementations

This commit is contained in:
SimoneN64
2025-01-06 22:03:39 +01:00
parent 7d18f2386a
commit 200f6c4515
6 changed files with 505 additions and 31 deletions

View File

@@ -23,7 +23,7 @@ void JIT::CheckCompareInterrupt() {
} }
int JIT::Step() { int JIT::Step() {
u32 instruction; u32 instruction = 0;
s64 pc = regs.pc; s64 pc = regs.pc;
do { do {
@@ -32,19 +32,23 @@ int JIT::Step() {
// regs.prevDelaySlot = regs.delaySlot; // regs.prevDelaySlot = regs.delaySlot;
// regs.delaySlot = false; // regs.delaySlot = false;
/*if (check_address_error(0b11, u64(pc))) [[unlikely]] { if (check_address_error(0b11, u64(pc))) [[unlikely]] {
regs.cop0.HandleTLBException(pc); /*regs.cop0.HandleTLBException(pc);
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, 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<u64>(regs.pc));
}
u32 paddr = 0; u32 paddr = 0;
if (!regs.cop0.MapVAddr(Cop0::LOAD, pc, paddr)) { if (!regs.cop0.MapVAddr(Cop0::LOAD, pc, paddr)) {
/*regs.cop0.HandleTLBException(pc); /*regs.cop0.HandleTLBException(pc);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, pc); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, pc);
return 1;*/ return 1;*/
Util::panic("[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!", Util::panic(
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD))); "[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address! (virtual: 0x{:016lX})",
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)), static_cast<u64>(regs.pc));
} }
instruction = mem.Read<u32>(regs, paddr); instruction = mem.Read<u32>(regs, paddr);
@@ -56,10 +60,12 @@ int JIT::Step() {
pc += 4; pc += 4;
// Exec(instruction); Emit(instruction);
} }
while (!InstrEndsBlock(instruction)); while (!InstrEndsBlock(instruction));
// emit code to store the value of pc
return 1; return 1;
} }

View File

@@ -2,10 +2,23 @@
#include <BaseCPU.hpp> #include <BaseCPU.hpp>
#include <Mem.hpp> #include <Mem.hpp>
#include <vector> #include <vector>
#include <xbyak.h>
namespace n64 { namespace n64 {
struct Core; 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 { struct JIT : BaseCPU {
explicit JIT(ParallelRDP &); explicit JIT(ParallelRDP &);
~JIT() override = default; ~JIT() override = default;
@@ -23,19 +36,21 @@ struct JIT : BaseCPU {
[[nodiscard]] Disassembler::DisassemblyResult Disassemble(u32, u32) const override { return {}; } [[nodiscard]] Disassembler::DisassemblyResult Disassemble(u32, u32) const override { return {}; }
private: private:
CodeGenerator code;
Registers regs; Registers regs;
Mem mem; Mem mem;
u64 cop2Latch{}; u64 cop2Latch{};
friend struct Cop1; friend struct Cop1;
#define check_address_error(mask, vaddr) \ #define check_address_error(mask, vaddr) \
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0)) (((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
bool ShouldServiceInterrupt() const override;
[[nodiscard]] bool ShouldServiceInterrupt() const override;
void CheckCompareInterrupt() override; void CheckCompareInterrupt() override;
std::vector<u8> Serialize() override; std::vector<u8> Serialize() override;
void Deserialize(const std::vector<u8> &) override; void Deserialize(const std::vector<u8> &) override;
void Emit(u32); void Emit(u32);
void cop2Decode(u32);
void special(u32); void special(u32);
void regimm(u32); void regimm(u32);
void add(u32); void add(u32);
@@ -44,12 +59,14 @@ private:
void addiu(u32); void addiu(u32);
void andi(u32); void andi(u32);
void and_(u32); void and_(u32);
void branch(bool, s64); void b(u32 instr, BranchCondition, u32 reg1, u32 reg2);
void branch_likely(bool, s64); void b(u32 instr, BranchCondition, u32 reg);
void b(u32, bool); void blink(u32 instr, BranchCondition, u32 reg1, u32 reg2);
void blink(u32, bool); void blink(u32 instr, BranchCondition, u32 reg);
void bl(u32, bool); void bl(u32 instr, BranchCondition, u32 reg1, u32 reg2);
void bllink(u32, bool); 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 dadd(u32);
void daddu(u32); void daddu(u32);
void daddi(u32); void daddi(u32);
@@ -79,6 +96,7 @@ private:
void lbu(u32); void lbu(u32);
void lb(u32); void lb(u32);
void ld(u32); void ld(u32);
void ldc1(u32);
void ldl(u32); void ldl(u32);
void ldr(u32); void ldr(u32);
void lh(u32); void lh(u32);
@@ -86,6 +104,7 @@ private:
void ll(u32); void ll(u32);
void lld(u32); void lld(u32);
void lw(u32); void lw(u32);
void lwc1(u32);
void lwl(u32); void lwl(u32);
void lwu(u32); void lwu(u32);
void lwr(u32); void lwr(u32);
@@ -100,6 +119,7 @@ private:
void sc(u32); void sc(u32);
void scd(u32); void scd(u32);
void sd(u32); void sd(u32);
void sdc1(u32);
void sdl(u32); void sdl(u32);
void sdr(u32); void sdr(u32);
void sh(u32); void sh(u32);
@@ -114,6 +134,7 @@ private:
void sllv(u32); void sllv(u32);
void sub(u32); void sub(u32);
void subu(u32); void subu(u32);
void swc1(u32);
void sra(u32); void sra(u32);
void srav(u32); void srav(u32);
void srl(u32); void srl(u32);
@@ -123,12 +144,5 @@ private:
void ori(u32); void ori(u32);
void xor_(u32); void xor_(u32);
void xori(u32); void xori(u32);
void mtc2(u32);
void mfc2(u32);
void dmtc2(u32);
void dmfc2(u32);
void ctc2(u32);
void cfc2(u32);
}; };
} // namespace n64 } // namespace n64

View File

@@ -0,0 +1,388 @@
#include <JIT.hpp>
#include <CpuDefinitions.hpp>
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<s64>(RS(instr)) >= regs.Read<s64>(RT(instr)));
break;
case TGEU:
trap(regs.Read<u64>(RS(instr)) >= regs.Read<u64>(RT(instr)));
break;
case TLT:
trap(regs.Read<s64>(RS(instr)) < regs.Read<s64>(RT(instr)));
break;
case TLTU:
trap(regs.Read<u64>(RS(instr)) < regs.Read<u64>(RT(instr)));
break;
case TEQ:
trap(regs.Read<s64>(RS(instr)) == regs.Read<s64>(RT(instr)));
break;
case TNE:
trap(regs.Read<s64>(RS(instr)) != regs.Read<s64>(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<u64>(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<s64>(RS(instr)) >= static_cast<s64>(static_cast<s16>(instr)));
break;
case TGEIU:
trap(regs.Read<u64>(RS(instr)) >= static_cast<u64>(static_cast<s64>(static_cast<s16>(instr))));
break;
case TLTI:
trap(regs.Read<s64>(RS(instr)) < static_cast<s64>(static_cast<s16>(instr)));
break;
case TLTIU:
trap(regs.Read<u64>(RS(instr)) < static_cast<u64>(static_cast<s64>(static_cast<s16>(instr))));
break;
case TEQI:
trap(regs.Read<s64>(RS(instr)) == static_cast<s64>(static_cast<s16>(instr)));
break;
case TNEI:
trap(regs.Read<s64>(RS(instr)) != static_cast<s64>(static_cast<s16>(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<u64>(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<u64>(regs.oldPC));
}
}
} // namespace n64

View File

@@ -1,9 +1,9 @@
#pragma once
#include <CpuDefinitions.hpp> #include <CpuDefinitions.hpp>
namespace n64 { namespace n64 {
static inline bool SpecialEndsBlock(u32 instr) { static bool SpecialEndsBlock(const u32 instr) {
u8 mask = instr & 0x3F; switch (instr & 0x3F) {
switch (mask) {
case JR: case JR:
case JALR: case JALR:
case SYSCALL: case SYSCALL:
@@ -20,9 +20,8 @@ static inline bool SpecialEndsBlock(u32 instr) {
} }
} }
static inline bool InstrEndsBlock(u32 instr) { static bool InstrEndsBlock(const u32 instr) {
u8 mask = (instr >> 26) & 0x3f; switch (instr >> 26 & 0x3f) {
switch (mask) {
case SPECIAL: case SPECIAL:
return SpecialEndsBlock(instr); return SpecialEndsBlock(instr);
case REGIMM: case REGIMM:

View File

@@ -79,6 +79,75 @@ void JIT::and_(u32 instr) {
} }
} }
void branch(Registers &regs, const bool cond, const s64 address) {
regs.delaySlot = true;
if (cond) {
regs.nextPC = address;
}
}
void branch_likely(Registers &regs, const bool cond, const s64 address) {
if (cond) {
regs.delaySlot = true;
regs.nextPC = address;
} else {
regs.SetPC64(regs.nextPC);
}
}
bool EvaluateCondition(Registers &regs, BranchCondition, u32, u32) {
Util::panic("[JIT]: non-constant EvaluateCondition!");
}
bool EvaluateConditionConstant(Registers &regs, const BranchCondition cond, const u32 reg1, const u32 reg2) {
switch (cond) {
case EQ:
return regs.Read<s64>(reg1) == regs.Read<s64>(reg2);
case NE:
return regs.Read<s64>(reg1) != regs.Read<s64>(reg2);
case LT:
return regs.Read<s64>(reg1) < regs.Read<s64>(reg2);
case LE:
return regs.Read<s64>(reg1) <= regs.Read<s64>(reg2);
case GT:
return regs.Read<s64>(reg1) > regs.Read<s64>(reg2);
case GE:
return regs.Read<s64>(reg1) >= regs.Read<s64>(reg2);
case LTU:
return regs.Read<u64>(reg1) < regs.Read<u64>(reg2);
case LEU:
return regs.Read<u64>(reg1) <= regs.Read<u64>(reg2);
case GTU:
return regs.Read<u64>(reg1) > regs.Read<u64>(reg2);
case GEU:
return regs.Read<u64>(reg1) >= regs.Read<u64>(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) { void JIT::dadd(u32 instr) {
if (regs.IsRegConstant(RS(instr), RT(instr))) { if (regs.IsRegConstant(RS(instr), RT(instr))) {
auto rs = regs.Read<u64>(RS(instr)); auto rs = regs.Read<u64>(RS(instr));

View File

@@ -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 { union FloatingPointReg {
struct { struct {
s32 int32; s32 int32;