Compare commits

..

2 Commits

Author SHA1 Message Date
iris 204f0e13b0 idle skipping seems to work! 2026-06-03 10:07:11 +02:00
iris cb8bb634ae sdkfjlasdf 2026-06-03 09:14:28 +02:00
6 changed files with 186 additions and 150 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ struct Core {
return instance; return instance;
} }
static inline bool IsAddressError(u8 mask, u64 vaddr) { static inline bool IsAddressError(u8 mask, s64 vaddr) {
auto regs = GetRegs(); auto regs = GetRegs();
return (!regs.cop0.is64BitAddressing && s32(vaddr) != vaddr) || (vaddr & mask) != 0; return (!regs.cop0.is64BitAddressing && s32(vaddr) != vaddr) || (vaddr & mask) != 0;
} }
+42 -3
View File
@@ -116,8 +116,8 @@ u32 Interpreter::CacheBlock(u32 addr) {
break; break;
} }
if (InstrEndsBlock(instr)) { if (instr.EndsBlock()) {
if (InstrHasDelaySlot(instr) && !fetchDelaySlot) { if (instr.HasDelaySlot() && !fetchDelaySlot) {
fetchDelaySlot = true; fetchDelaySlot = true;
continue; continue;
} }
@@ -129,6 +129,45 @@ u32 Interpreter::CacheBlock(u32 addr) {
} }
} }
auto delay = line.code[i - 1];
if (i == 2) {
// branch to itself
// _nop
auto branch = line.code[i - 2];
line.idleSkip = branch.IsBranch() && branch.offset() == -4 && delay.instr.raw == 0;
}
if (i == 3) {
// load reg1, [some location]
// bcond reg2
// _andi reg2, reg1, immediate
auto branch = line.code[i - 2];
auto load = line.code[i - 3]; // load
line.idleSkip = (load.opcode() == Instruction::LW || load.opcode() == Instruction::LWU) &&
delay.opcode() == Instruction::ANDI && branch.IsBranch() && branch.offset() == -16 &&
load.rt() == delay.rs() && delay.rt() == branch.rs();
}
if (i == 5) {
// lui reg1
// load reg2, [reg1(offset)]
// andi reg3, reg2, immediate
// bcond reg3
// _nop
auto branch = line.code[i - 2];
auto andi = line.code[i - 3]; // andi
auto load = line.code[i - 4]; // load
line.idleSkip = (load.opcode() == Instruction::LW || load.opcode() == Instruction::LWU) &&
andi.opcode() == Instruction::ANDI && branch.IsBranch() && branch.offset() == -16 &&
load.rt() == andi.rs() && andi.rt() == branch.rs();
}
line.cycles = i; line.cycles = i;
line.len = i; line.len = i;
cachedState.blocks[CACHE_GET_BLOCK(blockAddr)]->lines[CACHE_GET_LINE(blockAddr)] = new CachedLine(line); cachedState.blocks[CACHE_GET_BLOCK(blockAddr)]->lines[CACHE_GET_LINE(blockAddr)] = new CachedLine(line);
@@ -162,7 +201,7 @@ u32 Interpreter::ExecuteCached() {
return i + 1; return i + 1;
// Branch likely with false condition, it wasn't taken so don't execute the delay slot // Branch likely with false condition, it wasn't taken so don't execute the delay slot
if (IsBranchLikely(instr) && !regs.delaySlot) if (instr.IsBranchLikely() && !regs.delaySlot)
break; break;
} }
+5 -5
View File
@@ -38,7 +38,7 @@ void JIT::InvalidateBlock(const u32 paddr) {
blockCache[index] = {}; blockCache[index] = {};
} }
std::optional<u32> JIT::FetchInstruction(s64 vaddr) { std::optional<Instruction> JIT::FetchInstruction(s64 vaddr) {
u32 paddr = 0; u32 paddr = 0;
if (Core::IsAddressError(0b11, vaddr)) [[unlikely]] { if (Core::IsAddressError(0b11, vaddr)) [[unlikely]] {
@@ -62,7 +62,7 @@ std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
return std::nullopt; return std::nullopt;
} }
const u32 instr = Core::GetMem().Read<u32>(paddr); const Instruction instr = Core::GetMem().Read<u32>(paddr);
info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full); info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full);
@@ -169,12 +169,12 @@ u32 JIT::Step() {
blockPC = blockNextPC; blockPC = blockNextPC;
blockNextPC += 4; blockNextPC += 4;
if (InstrEndsBlock(instruction.value())) { if (instruction.value().EndsBlock()) {
const auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot
if (!delay_instruction) if (!delay_instruction)
return 0; return 0;
if (InstrEndsBlock(delay_instruction.value())) { if (delay_instruction.value().EndsBlock()) {
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
"[JIT]: Unhandled case of branch from delay slot!"); "[JIT]: Unhandled case of branch from delay slot!");
+1 -1
View File
@@ -118,7 +118,7 @@ struct JIT final {
[[nodiscard]] bool ShouldServiceInterrupt() const; [[nodiscard]] bool ShouldServiceInterrupt() const;
void CheckCompareInterrupt() const; void CheckCompareInterrupt() const;
std::optional<u32> FetchInstruction(s64); std::optional<Instruction> FetchInstruction(s64);
void Emit(Instruction); void Emit(Instruction);
void special(Instruction); void special(Instruction);
-138
View File
@@ -2,144 +2,6 @@
#include <Instruction.hpp> #include <Instruction.hpp>
namespace n64 { namespace n64 {
static bool SpecialEndsBlock(const Instruction instr) {
switch (instr.special()) {
case Instruction::JR:
case Instruction::JALR:
case Instruction::SYSCALL:
case Instruction::BREAK:
case Instruction::TGE:
case Instruction::TGEU:
case Instruction::TLT:
case Instruction::TLTU:
case Instruction::TEQ:
case Instruction::TNE:
return true;
default:
return false;
}
}
static bool InstrHasDelaySlot(const Instruction instr) {
switch (instr.opcode()) {
case Instruction::SPECIAL:
if (instr.special() == Instruction::JR || instr.special() == Instruction::JALR)
return true;
return false;
case Instruction::REGIMM:
case Instruction::J:
case Instruction::JAL:
case Instruction::BEQ:
case Instruction::BNE:
case Instruction::BLEZ:
case Instruction::BGTZ:
case Instruction::BEQL:
case Instruction::BNEL:
case Instruction::BLEZL:
case Instruction::BGTZL:
return true;
case Instruction::COP1:
if (instr.cop_rs() == 8)
return true;
return false;
default:
return false;
}
}
static bool InstrEndsBlock(const Instruction instr) {
switch (instr.opcode()) {
case Instruction::SPECIAL:
return SpecialEndsBlock(instr);
case Instruction::REGIMM:
case Instruction::J:
case Instruction::JAL:
case Instruction::BEQ:
case Instruction::BNE:
case Instruction::BLEZ:
case Instruction::BGTZ:
case Instruction::BEQL:
case Instruction::BNEL:
case Instruction::BLEZL:
case Instruction::BGTZL:
return true;
case Instruction::COP1:
if (instr.cop_rs() == 8)
return true;
return false;
case Instruction::COP0:
switch (instr.cop_rs()) {
case 0x10 ... 0x1F:
switch (instr.cop_funct()) {
case 0x18: // eret
return true;
default:
return false;
}
default:
return false;
}
default:
return false;
}
}
static bool IsBranchLikely(const Instruction instr) {
switch (instr.opcode()) {
case Instruction::BEQL:
case Instruction::BNEL:
case Instruction::BLEZL:
case Instruction::BGTZL:
return true;
case Instruction::REGIMM:
switch (instr.regimm()) {
case Instruction::BLTZL:
case Instruction::BGEZL:
case Instruction::BLTZALL:
case Instruction::BGEZALL:
return true;
default:
return false;
}
case Instruction::COP1:
if (instr.cop_rs() == 8 && (instr.cop_rt() == 2 || instr.cop_rt() == 3))
return true;
return false;
default:
return false;
}
}
static bool IsBranch(const Instruction instr) {
switch (instr.opcode()) {
case Instruction::BEQ:
case Instruction::BNE:
case Instruction::BLEZ:
case Instruction::BGTZ:
return true;
case Instruction::REGIMM:
switch (instr.regimm()) {
case Instruction::BLTZ:
case Instruction::BGEZ:
case Instruction::BLTZAL:
case Instruction::BGEZAL:
return true;
default:
return false;
}
case Instruction::COP1:
if (instr.cop_rs() == 8 && (instr.cop_rt() == 0 || instr.cop_rt() == 1))
return true;
return false;
default:
return false;
}
}
#ifdef _WIN32 #ifdef _WIN32
#define ARG1 rcx #define ARG1 rcx
#define ARG2 rdx #define ARG2 rdx
+137 -2
View File
@@ -24,8 +24,8 @@ struct Instruction {
inline u8 e1() const { return (instr.raw >> 7) & 0x0f; } inline u8 e1() const { return (instr.raw >> 7) & 0x0f; }
inline u8 e2() const { return rs() & 0x0f; } inline u8 e2() const { return rs() & 0x0f; }
inline u8 op() const { return rt(); } inline u8 op() const { return rt(); }
inline u16 imm() const { return instr.itype.imm; } inline s16 imm() const { return instr.itype.imm; }
inline u16 offset() const { return imm(); } inline s64 offset() const { return s64(imm()) << 2; }
inline u32 target() const { return instr.jtype.target; } inline u32 target() const { return instr.jtype.target; }
inline u8 opcode() const { return instr.opcode.op; } inline u8 opcode() const { return instr.opcode.op; }
inline u8 special() const { return instr.opcode.special; } inline u8 special() const { return instr.opcode.special; }
@@ -99,6 +99,141 @@ struct Instruction {
u32 raw; u32 raw;
} instr{}; } instr{};
bool IsBranch() {
switch (opcode()) {
case Instruction::BEQ:
case Instruction::BNE:
case Instruction::BLEZ:
case Instruction::BGTZ:
return true;
case Instruction::REGIMM:
switch (regimm()) {
case Instruction::BLTZ:
case Instruction::BGEZ:
case Instruction::BLTZAL:
case Instruction::BGEZAL:
return true;
default:
return false;
}
case Instruction::COP1:
if (cop_rs() == 8 && (cop_rt() == 0 || cop_rt() == 1))
return true;
return false;
default:
return false;
}
}
bool HasDelaySlot() {
switch (opcode()) {
case Instruction::SPECIAL:
if (special() == Instruction::JR || special() == Instruction::JALR)
return true;
return false;
case Instruction::REGIMM:
case Instruction::J:
case Instruction::JAL:
case Instruction::BEQ:
case Instruction::BNE:
case Instruction::BLEZ:
case Instruction::BGTZ:
case Instruction::BEQL:
case Instruction::BNEL:
case Instruction::BLEZL:
case Instruction::BGTZL:
return true;
case Instruction::COP1:
if (cop_rs() == 8)
return true;
return false;
default:
return false;
}
}
bool EndsBlock() {
switch (opcode()) {
case Instruction::SPECIAL:
switch (special()) {
case Instruction::JR:
case Instruction::JALR:
case Instruction::SYSCALL:
case Instruction::BREAK:
case Instruction::TGE:
case Instruction::TGEU:
case Instruction::TLT:
case Instruction::TLTU:
case Instruction::TEQ:
case Instruction::TNE:
return true;
default:
return false;
}
case Instruction::REGIMM:
case Instruction::J:
case Instruction::JAL:
case Instruction::BEQ:
case Instruction::BNE:
case Instruction::BLEZ:
case Instruction::BGTZ:
case Instruction::BEQL:
case Instruction::BNEL:
case Instruction::BLEZL:
case Instruction::BGTZL:
return true;
case Instruction::COP1:
if (cop_rs() == 8)
return true;
return false;
case Instruction::COP0:
switch (cop_rs()) {
case 0x10 ... 0x1F:
switch (cop_funct()) {
case 0x18: // eret
return true;
default:
return false;
}
default:
return false;
}
default:
return false;
}
}
bool IsBranchLikely() {
switch (opcode()) {
case Instruction::BEQL:
case Instruction::BNEL:
case Instruction::BLEZL:
case Instruction::BGTZL:
return true;
case Instruction::REGIMM:
switch (regimm()) {
case Instruction::BLTZL:
case Instruction::BGEZL:
case Instruction::BLTZALL:
case Instruction::BGEZALL:
return true;
default:
return false;
}
case Instruction::COP1:
if (cop_rs() == 8 && (cop_rt() == 2 || cop_rt() == 3))
return true;
return false;
default:
return false;
}
}
static constexpr u8 SPECIAL = 0b000000; static constexpr u8 SPECIAL = 0b000000;
static constexpr u8 REGIMM = 0b000001; static constexpr u8 REGIMM = 0b000001;
static constexpr u8 J = 0b000010; static constexpr u8 J = 0b000010;