diff --git a/src/backend/core/Interpreter.cpp b/src/backend/core/Interpreter.cpp index 0038fd8..b5b597f 100644 --- a/src/backend/core/Interpreter.cpp +++ b/src/backend/core/Interpreter.cpp @@ -116,8 +116,8 @@ u32 Interpreter::CacheBlock(u32 addr) { break; } - if (InstrEndsBlock(instr)) { - if (InstrHasDelaySlot(instr) && !fetchDelaySlot) { + if (instr.EndsBlock()) { + if (instr.HasDelaySlot() && !fetchDelaySlot) { fetchDelaySlot = true; 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.len = i; cachedState.blocks[CACHE_GET_BLOCK(blockAddr)]->lines[CACHE_GET_LINE(blockAddr)] = new CachedLine(line); @@ -162,7 +201,7 @@ u32 Interpreter::ExecuteCached() { return i + 1; // 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; } diff --git a/src/backend/core/JIT.cpp b/src/backend/core/JIT.cpp index 553321d..97cbc3b 100644 --- a/src/backend/core/JIT.cpp +++ b/src/backend/core/JIT.cpp @@ -38,7 +38,7 @@ void JIT::InvalidateBlock(const u32 paddr) { blockCache[index] = {}; } -std::optional JIT::FetchInstruction(s64 vaddr) { +std::optional JIT::FetchInstruction(s64 vaddr) { u32 paddr = 0; if (Core::IsAddressError(0b11, vaddr)) [[unlikely]] { @@ -62,7 +62,7 @@ std::optional JIT::FetchInstruction(s64 vaddr) { return std::nullopt; } - const u32 instr = Core::GetMem().Read(paddr); + const Instruction instr = Core::GetMem().Read(paddr); info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full); @@ -169,12 +169,12 @@ u32 JIT::Step() { blockPC = blockNextPC; blockNextPC += 4; - if (InstrEndsBlock(instruction.value())) { - const auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot + if (instruction.value().EndsBlock()) { + auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot if (!delay_instruction) return 0; - if (InstrEndsBlock(delay_instruction.value())) { + if (delay_instruction.value().EndsBlock()) { 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!"); diff --git a/src/backend/core/JIT.hpp b/src/backend/core/JIT.hpp index 83a546b..e6b0a67 100644 --- a/src/backend/core/JIT.hpp +++ b/src/backend/core/JIT.hpp @@ -118,7 +118,7 @@ struct JIT final { [[nodiscard]] bool ShouldServiceInterrupt() const; void CheckCompareInterrupt() const; - std::optional FetchInstruction(s64); + std::optional FetchInstruction(s64); void Emit(Instruction); void special(Instruction); diff --git a/src/backend/core/jit/helpers.hpp b/src/backend/core/jit/helpers.hpp index 39806fe..030a91b 100644 --- a/src/backend/core/jit/helpers.hpp +++ b/src/backend/core/jit/helpers.hpp @@ -2,144 +2,6 @@ #include 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 #define ARG1 rcx #define ARG2 rdx diff --git a/src/utils/Instruction.hpp b/src/utils/Instruction.hpp index ef4fc30..ad144ca 100644 --- a/src/utils/Instruction.hpp +++ b/src/utils/Instruction.hpp @@ -24,8 +24,8 @@ struct Instruction { inline u8 e1() const { return (instr.raw >> 7) & 0x0f; } inline u8 e2() const { return rs() & 0x0f; } inline u8 op() const { return rt(); } - inline u16 imm() const { return instr.itype.imm; } - inline u16 offset() const { return imm(); } + inline s16 imm() const { return instr.itype.imm; } + inline s64 offset() const { return s64(imm()) << 2; } inline u32 target() const { return instr.jtype.target; } inline u8 opcode() const { return instr.opcode.op; } inline u8 special() const { return instr.opcode.special; } @@ -99,6 +99,141 @@ struct Instruction { u32 raw; } 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 REGIMM = 0b000001; static constexpr u8 J = 0b000010;