Revert "Merge branch 'jit' of https://github.com/SimoneN64/Kaizen into dev"
This reverts commit28b3c4493f, reversing changes made tof4123da691.
This commit is contained in:
@@ -26,4 +26,4 @@ find_package(Qt6 COMPONENTS Core Gui Widgets)
|
||||
|
||||
if (Qt6_FOUND)
|
||||
add_subdirectory(src/frontend)
|
||||
endif()
|
||||
endif()
|
||||
@@ -65,7 +65,7 @@ void Core::Run(float volumeL, float volumeR) {
|
||||
InterruptRaise(mmio.mi, regs, Interrupt::VI);
|
||||
}
|
||||
|
||||
for(; cycles < mem.mmio.vi.cyclesPerHalfline;) {
|
||||
for(; cycles < mem.mmio.vi.cyclesPerHalfline; cycles++, frameCycles++) {
|
||||
u32 taken = cpu->Step();
|
||||
taken += PopStalledCycles();
|
||||
static u32 cpuSteps = 0;
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
#include <Cop0.hpp>
|
||||
#include <JIT.hpp>
|
||||
|
||||
namespace n64 {
|
||||
using namespace Xbyak;
|
||||
JIT::JIT() : CodeGenerator(32*1024*1024, AutoGrow) { }
|
||||
|
||||
void JIT::Reset() {
|
||||
reset();
|
||||
regs.Reset();
|
||||
mem.Reset();
|
||||
}
|
||||
|
||||
bool JIT::ShouldServiceInterrupt() {
|
||||
bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0;
|
||||
bool interrupts_enabled = regs.cop0.status.ie == 1;
|
||||
bool currently_handling_exception = regs.cop0.status.exl == 1;
|
||||
bool currently_handling_error = regs.cop0.status.erl == 1;
|
||||
|
||||
return interrupts_pending && interrupts_enabled &&
|
||||
!currently_handling_exception && !currently_handling_error;
|
||||
}
|
||||
|
||||
void JIT::CheckCompareInterrupt() {
|
||||
regs.cop0.count++;
|
||||
regs.cop0.count &= 0x1FFFFFFFF;
|
||||
if(regs.cop0.count == (u64)regs.cop0.compare << 1) {
|
||||
regs.cop0.cause.ip7 = 1;
|
||||
UpdateInterrupt(mem.mmio.mi, regs);
|
||||
}
|
||||
}
|
||||
|
||||
Fn JIT::Recompile() {
|
||||
bool stable = true;
|
||||
bool old_stable = stable;
|
||||
cycles = 0;
|
||||
//prologue();
|
||||
//mov(rbp, u64(this));
|
||||
//mov(rdi, u64(this) + THIS_OFFSET(regs));
|
||||
u64 pc = regs.pc;
|
||||
while(old_stable) {
|
||||
old_stable = stable;
|
||||
|
||||
cycles++;
|
||||
CheckCompareInterrupt();
|
||||
|
||||
// mov(rax, REG(byte, delaySlot));
|
||||
// mov(REG(byte, prevDelaySlot), rax);
|
||||
// mov(REG(byte, delaySlot), 0);
|
||||
|
||||
u32 paddr = 0;
|
||||
if (!MapVAddr(regs, LOAD, pc, paddr)) {
|
||||
//mov(rsi, regs.pc);
|
||||
//emitCall(HandleTLBException);
|
||||
//mov(rsi, u64(GetTLBExceptionCode(regs.cop0.tlbError, LOAD)));
|
||||
//CodeGenerator::xor_(rdx, rdx);
|
||||
//CodeGenerator::xor_(rcx, rcx);
|
||||
//emitCall(FireException);
|
||||
//goto _epilogue;
|
||||
}
|
||||
|
||||
pc += 4;
|
||||
u32 instr = mem.Read<u32>(regs, paddr);
|
||||
stable = isStable(instr);
|
||||
Emit(instr);
|
||||
|
||||
if (ShouldServiceInterrupt()) {
|
||||
//mov(rsi, u64(ExceptionCode::Interrupt));
|
||||
//CodeGenerator::xor_(rdx, rdx);
|
||||
//CodeGenerator::xor_(rcx, rcx);
|
||||
//push(rax);
|
||||
//call(FireException);
|
||||
//goto _epilogue;
|
||||
}
|
||||
|
||||
//mov(rax, REG(qword, pc));
|
||||
//mov(REG(qword, oldPC), rax);
|
||||
//mov(rax, REG(qword, nextPC));
|
||||
//mov(REG(qword, pc), rax);
|
||||
//CodeGenerator::add(REG(qword, nextPC), 4);
|
||||
}
|
||||
_epilogue:
|
||||
//epilogue();
|
||||
//ready();
|
||||
//return getCode<Fn>();
|
||||
ir.optimize();
|
||||
ir.print();
|
||||
exit(1);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int JIT::Step() {
|
||||
if(!blocks[BLOCKCACHE_OUTER_INDEX(regs.pc)]) {
|
||||
blocks[BLOCKCACHE_OUTER_INDEX(regs.pc)] = (Fn*)calloc(BLOCKCACHE_INNER_SIZE, 1);
|
||||
blocks[BLOCKCACHE_OUTER_INDEX(regs.pc)][BLOCKCACHE_INNER_INDEX(regs.pc)] = Recompile();
|
||||
}
|
||||
|
||||
if (!blocks[BLOCKCACHE_OUTER_INDEX(regs.pc)][BLOCKCACHE_INNER_INDEX(regs.pc)]) {
|
||||
blocks[BLOCKCACHE_OUTER_INDEX(regs.pc)][BLOCKCACHE_INNER_INDEX(regs.pc)] = Recompile();
|
||||
}
|
||||
|
||||
//return blocks[BLOCKCACHE_OUTER_INDEX(regs.pc)][BLOCKCACHE_INNER_INDEX(regs.pc)]();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
#pragma once
|
||||
#include <Mem.hpp>
|
||||
#include <vector>
|
||||
#include <BaseCPU.hpp>
|
||||
#include <xbyak.h>
|
||||
#include <CpuDefinitions.hpp>
|
||||
#include <IR.hpp>
|
||||
|
||||
namespace n64 {
|
||||
using Fn = int(*)();
|
||||
#define THIS_OFFSET(x) ((uintptr_t)(&x) - (uintptr_t)this)
|
||||
#define GPR_OFFSET(x) ((uintptr_t)®s.gpr[(x)] - (uintptr_t)this)
|
||||
#define REG_OFFSET(member) ((uintptr_t)®s.member - (uintptr_t)this)
|
||||
#define GPR(ptr, x) ptr[rdi + GPR_OFFSET(x)]
|
||||
#define REG(ptr, member) ptr[rdi + REG_OFFSET(member)]
|
||||
// 4KiB aligned pages
|
||||
#define BLOCKCACHE_OUTER_SHIFT 12
|
||||
#define BLOCKCACHE_PAGE_SIZE (1 << BLOCKCACHE_OUTER_SHIFT)
|
||||
#define BLOCKCACHE_OUTER_SIZE (0x80000000 >> BLOCKCACHE_OUTER_SHIFT)
|
||||
// word aligned instructions
|
||||
#define BLOCKCACHE_INNER_SIZE (BLOCKCACHE_PAGE_SIZE >> 2)
|
||||
#define BLOCKCACHE_INNER_INDEX(physical) (((physical) & (BLOCKCACHE_PAGE_SIZE - 1)) >> 2)
|
||||
#define BLOCKCACHE_OUTER_INDEX(physical) ((physical) >> BLOCKCACHE_OUTER_SHIFT)
|
||||
|
||||
struct JIT : BaseCPU, Xbyak::CodeGenerator {
|
||||
JIT();
|
||||
~JIT() override = default;
|
||||
int Step() override;
|
||||
void Reset() override;
|
||||
friend struct Cop1;
|
||||
friend struct Cop0;
|
||||
private:
|
||||
IR ir{};
|
||||
int cycles = 0;
|
||||
bool ShouldServiceInterrupt() override;
|
||||
void CheckCompareInterrupt() override;
|
||||
Fn Recompile();
|
||||
|
||||
template<class T>
|
||||
void emitMemberCall(T func, void* thisObj) {
|
||||
T* funcPtr;
|
||||
auto thisPtr = reinterpret_cast<uintptr_t>(thisObj);
|
||||
#ifdef ABI_WINDOWS
|
||||
static_assert(sizeof(T) == 8, "[JIT]: Invalid size for member function pointer");
|
||||
std::memcpy(&funcPtr, &func, sizeof(T));
|
||||
#elif defined(ABI_UNIX)
|
||||
static_assert(sizeof(T) == 16, "[JIT]: Invalid size for member function pointer");
|
||||
uintptr_t tmpArr[2];
|
||||
std::memcpy(tmpArr, &func, sizeof(T));
|
||||
funcPtr = reinterpret_cast<void*>(tmpArr[0]);
|
||||
thisPtr += tmpArr[1];
|
||||
#else
|
||||
Util::panic("Huh?!");
|
||||
#endif
|
||||
|
||||
push(rdi);
|
||||
if(thisPtr == reinterpret_cast<uintptr_t>(this)) {
|
||||
mov(rdi, rbp);
|
||||
} else {
|
||||
mov(rdi, (uintptr_t)thisPtr);
|
||||
}
|
||||
call(funcPtr);
|
||||
pop(rdi);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void emitCall(T func) {
|
||||
T* funcPtr;
|
||||
#ifdef ABI_WINDOWS
|
||||
std::memcpy(&funcPtr, &func, sizeof(T));
|
||||
#elif defined(ABI_UNIX)
|
||||
uintptr_t tmpArr[2];
|
||||
std::memcpy(tmpArr, &func, sizeof(T));
|
||||
funcPtr = reinterpret_cast<void*>(tmpArr[0]);
|
||||
#else
|
||||
Util::panic("Huh?!");
|
||||
#endif
|
||||
|
||||
push(rdi);
|
||||
call(funcPtr);
|
||||
pop(rdi);
|
||||
}
|
||||
|
||||
bool isStable(u32 instr) {
|
||||
u8 mask = (instr >> 26) & 0x3f;
|
||||
switch(mask) {
|
||||
case SPECIAL:
|
||||
mask = instr & 0x3f;
|
||||
switch(mask) {
|
||||
case JR ... JALR:
|
||||
case SYSCALL: case BREAK:
|
||||
case TGE ... TNE:
|
||||
return false;
|
||||
default: return true;
|
||||
}
|
||||
case REGIMM:
|
||||
case J ... BGTZ:
|
||||
case BEQL ... BGTZL:
|
||||
return false;
|
||||
case COP1:
|
||||
mask = (instr >> 16) & 0x1f;
|
||||
if(mask >= 0 && mask <= 3) {
|
||||
return false;
|
||||
}
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void prologue() {
|
||||
const Xbyak::Reg64 allRegs[]{rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15};
|
||||
for(auto r : allRegs) {
|
||||
push(r);
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void epilogue() {
|
||||
const Xbyak::Reg64 allRegs[]{r15, r14, r13, r12, r11, r10, r9, r8, rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax};
|
||||
for(auto r : allRegs) {
|
||||
pop(r);
|
||||
}
|
||||
mov(rax, cycles);
|
||||
ret();
|
||||
}
|
||||
|
||||
Fn* blocks[BLOCKCACHE_OUTER_SIZE]{};
|
||||
|
||||
std::vector<u8> Serialize() override { return {}; }
|
||||
void Deserialize(const std::vector<u8>&) override { }
|
||||
|
||||
void cop2Decode(u32);
|
||||
void special(u32);
|
||||
void regimm(u32);
|
||||
void Emit(u32);
|
||||
void add(u32);
|
||||
void addu(u32);
|
||||
void addi(u32);
|
||||
void addiu(u32);
|
||||
void andi(u32);
|
||||
void and_(u32);
|
||||
Entry::Operand branch(u32);
|
||||
void bltz(u32);
|
||||
void bgez(u32);
|
||||
void bltzl(u32);
|
||||
void bgezl(u32);
|
||||
void bltzal(u32);
|
||||
void bgezal(u32);
|
||||
void bltzall(u32);
|
||||
void bgezall(u32);
|
||||
void beq(u32);
|
||||
void bne(u32);
|
||||
void blez(u32);
|
||||
void bgtz(u32);
|
||||
void beql(u32);
|
||||
void bnel(u32);
|
||||
void blezl(u32);
|
||||
void bgtzl(u32);
|
||||
void dadd(u32);
|
||||
void daddu(u32);
|
||||
void daddi(u32);
|
||||
void daddiu(u32);
|
||||
void ddiv(u32);
|
||||
void ddivu(u32);
|
||||
void div(u32);
|
||||
void divu(u32);
|
||||
void dmult(u32);
|
||||
void dmultu(u32);
|
||||
void dsll(u32);
|
||||
void dsllv(u32);
|
||||
void dsll32(u32);
|
||||
void dsra(u32);
|
||||
void dsrav(u32);
|
||||
void dsra32(u32);
|
||||
void dsrl(u32);
|
||||
void dsrlv(u32);
|
||||
void dsrl32(u32);
|
||||
void dsub(u32);
|
||||
void dsubu(u32);
|
||||
void j(u32);
|
||||
void jr(u32);
|
||||
void jal(u32);
|
||||
void jalr(u32);
|
||||
void lui(u32);
|
||||
void lbu(u32);
|
||||
void lb(u32);
|
||||
void ld(u32);
|
||||
void ldl(u32);
|
||||
void ldr(u32);
|
||||
void lh(u32);
|
||||
void lhu(u32);
|
||||
void ll(u32);
|
||||
void lld(u32);
|
||||
void lw(u32);
|
||||
void lwl(u32);
|
||||
void lwu(u32);
|
||||
void lwr(u32);
|
||||
void mfhi(u32);
|
||||
void mflo(u32);
|
||||
void mult(u32);
|
||||
void multu(u32);
|
||||
void mthi(u32);
|
||||
void mtlo(u32);
|
||||
void nor(u32);
|
||||
void sb(u32);
|
||||
void sc(u32);
|
||||
void scd(u32);
|
||||
void sd(u32);
|
||||
void sdl(u32);
|
||||
void sdr(u32);
|
||||
void sh(u32);
|
||||
void sw(u32);
|
||||
void swl(u32);
|
||||
void swr(u32);
|
||||
void slti(u32);
|
||||
void sltiu(u32);
|
||||
void slt(u32);
|
||||
void sltu(u32);
|
||||
void sll(u32);
|
||||
void sllv(u32);
|
||||
void sub(u32);
|
||||
void subu(u32);
|
||||
void sra(u32);
|
||||
void srav(u32);
|
||||
void srl(u32);
|
||||
void srlv(u32);
|
||||
void tgei(u32);
|
||||
void tgeiu(u32);
|
||||
void tlti(u32);
|
||||
void tltiu(u32);
|
||||
void teqi(u32);
|
||||
void tnei(u32);
|
||||
void tge(u32);
|
||||
void tgeu(u32);
|
||||
void tlt(u32);
|
||||
void tltu(u32);
|
||||
void teq(u32);
|
||||
void tne(u32);
|
||||
void or_(u32);
|
||||
void ori(u32);
|
||||
void xor_(u32);
|
||||
void xori(u32);
|
||||
|
||||
void mtc0(u32);
|
||||
void dmtc0(u32);
|
||||
void mfc0(u32);
|
||||
void dmfc0(u32);
|
||||
void eret();
|
||||
|
||||
void tlbr();
|
||||
void tlbw(int);
|
||||
void tlbp();
|
||||
|
||||
void mtc2(u32);
|
||||
void mfc2(u32);
|
||||
void dmtc2(u32);
|
||||
void dmfc2(u32);
|
||||
void ctc2(u32);
|
||||
void cfc2(u32);
|
||||
};
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
#include <IR.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
namespace n64 {
|
||||
template <> struct fmt::formatter<Entry> : formatter<string_view> {
|
||||
auto format(Entry e, format_context& ctx) const {
|
||||
std::string op = "Unknown";
|
||||
switch (e.op) {
|
||||
case Entry::MOV: op = "MOV"; break;
|
||||
case Entry::ADD: op = "ADD"; break;
|
||||
case Entry::SUB: op = "SUB"; break;
|
||||
case Entry::UMUL: op = "UMUL"; break;
|
||||
case Entry::SMUL: op = "SMUL"; break;
|
||||
case Entry::DIV: op = "DIV"; break;
|
||||
case Entry::AND: op = "AND"; break;
|
||||
case Entry::NOR: op = "NOR"; break;
|
||||
case Entry::XOR: op = "XOR"; break;
|
||||
case Entry::OR: op = "OR"; break;
|
||||
case Entry::SRL: op = "SRL"; break;
|
||||
case Entry::SLL: op = "SLL"; break;
|
||||
case Entry::SRA: op = "SRA"; break;
|
||||
case Entry::LOADS8: op = "LOADS8"; break;
|
||||
case Entry::LOADS8_SHIFT: op = "LOADS8_SHIFT"; break;
|
||||
case Entry::STORE8: op = "STORE8"; break;
|
||||
case Entry::STORE8_SHIFT: op = "STORE8_SHIFT"; break;
|
||||
case Entry::LOADS16: op = "LOADS16"; break;
|
||||
case Entry::LOADS16_SHIFT: op = "LOADS16_SHIFT"; break;
|
||||
case Entry::STORE16: op = "STORE16"; break;
|
||||
case Entry::STORE16_SHIFT: op = "STORE16_SHIFT"; break;
|
||||
case Entry::LOADS32: op = "LOADS32"; break;
|
||||
case Entry::LOADS32_SHIFT: op = "LOADS32_SHIFT"; break;
|
||||
case Entry::STORE32: op = "STORE32"; break;
|
||||
case Entry::STORE32_SHIFT: op = "STORE32_SHIFT"; break;
|
||||
case Entry::LOADS64: op = "LOADS64"; break;
|
||||
case Entry::LOADS64_SHIFT: op = "LOADS64_SHIFT"; break;
|
||||
case Entry::STORE64: op = "STORE64"; break;
|
||||
case Entry::STORE64_SHIFT: op = "STORE64_SHIFT"; break;
|
||||
case Entry::LOADU8: op = "LOADU8"; break;
|
||||
case Entry::LOADU8_SHIFT: op = "LOADU8_SHIFT"; break;
|
||||
case Entry::LOADU16: op = "LOADU16"; break;
|
||||
case Entry::LOADU16_SHIFT: op = "LOADU16_SHIFT"; break;
|
||||
case Entry::LOADU32: op = "LOADU32"; break;
|
||||
case Entry::LOADU32_SHIFT: op = "LOADU32_SHIFT"; break;
|
||||
case Entry::LOADU64: op = "LOADU64"; break;
|
||||
case Entry::LOADU64_SHIFT: op = "LOADU64_SHIFT"; break;
|
||||
case Entry::BRANCH: op = "BRANCH"; break;
|
||||
case Entry::JUMP: op = "JUMP"; break;
|
||||
case Entry::MTC0: op = "MTC0"; break;
|
||||
case Entry::MFC0: op = "MFC0"; break;
|
||||
case Entry::SLT: op = "SLT"; break;
|
||||
}
|
||||
|
||||
bool put_comma = false;
|
||||
op += " ";
|
||||
if (e.dst.isReg()) {
|
||||
if (e.dst.index_or_imm.has_value()) {
|
||||
std::string dst = fmt::format("R{}", e.dst.index_or_imm.value());
|
||||
op += dst;
|
||||
put_comma = true;
|
||||
}
|
||||
} else if(e.dst.isImm()) {
|
||||
std::string dst = fmt::format("0x{:0X}", e.dst.index_or_imm.value());
|
||||
op += dst;
|
||||
put_comma = true;
|
||||
} else if(e.dst.type == Entry::Operand::PC64) {
|
||||
std::string dst = fmt::format("PC");
|
||||
op += dst;
|
||||
put_comma = true;
|
||||
} else {
|
||||
if(e.dst.type != Entry::Operand::NONE) {
|
||||
std::string dst = fmt::format("(0x{:0X})", e.dst.index_or_imm.value());
|
||||
op += dst;
|
||||
put_comma = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.bOffs.index_or_imm.has_value()) {
|
||||
std::string dst;
|
||||
if (e.bOffs.isReg()) {
|
||||
dst = fmt::format("R{}", e.bOffs.index_or_imm.value());
|
||||
} else if (e.bOffs.isImm()) {
|
||||
dst = fmt::format("0x{:0X}", e.bOffs.index_or_imm.value());
|
||||
}
|
||||
op += dst;
|
||||
put_comma = true;
|
||||
}
|
||||
|
||||
if (e.op1.isReg()) {
|
||||
if (e.op1.index_or_imm.has_value()) {
|
||||
std::string op1 = fmt::format("R{}", e.op1.index_or_imm.value());
|
||||
if(put_comma) {
|
||||
op += ", ";
|
||||
}
|
||||
op += op1;
|
||||
put_comma = true;
|
||||
}
|
||||
} else if(e.op1.isImm()) {
|
||||
if (e.op1.index_or_imm.has_value()) {
|
||||
std::string op1 = fmt::format("0x{:0X}", e.op1.index_or_imm.value());
|
||||
if(put_comma) {
|
||||
op += ", ";
|
||||
}
|
||||
op += op1;
|
||||
put_comma = true;
|
||||
}
|
||||
} else if (e.dst.type == Entry::Operand::PC64) {
|
||||
std::string dst = fmt::format("PC");
|
||||
if (put_comma) {
|
||||
op += ", ";
|
||||
}
|
||||
op += dst;
|
||||
put_comma = true;
|
||||
} else {
|
||||
if (e.op1.index_or_imm.has_value()) {
|
||||
std::string op1 = fmt::format("(0x{:0X})", e.op1.index_or_imm.value());
|
||||
if(put_comma) {
|
||||
op += ", ";
|
||||
}
|
||||
op += op1;
|
||||
put_comma = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.branchCond.has_value()) {
|
||||
put_comma = false;
|
||||
op += " ";
|
||||
switch (e.branchCond.value()) {
|
||||
case Entry::AL: op += " "; break;
|
||||
case Entry::EQ: op += "== "; break;
|
||||
case Entry::NE: op += "!= "; break;
|
||||
case Entry::LT: op += "< "; break;
|
||||
case Entry::GT: op += "> "; break;
|
||||
case Entry::LE: op += "<= "; break;
|
||||
case Entry::GE: op += ">= "; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.op2.isReg()) {
|
||||
if (e.op2.index_or_imm.has_value()) {
|
||||
std::string op2 = fmt::format("R{}", e.op2.index_or_imm.value());
|
||||
if(put_comma) {
|
||||
op += ", ";
|
||||
}
|
||||
op += op2;
|
||||
}
|
||||
} else {
|
||||
if (e.op2.index_or_imm.has_value()) {
|
||||
std::string op2 = fmt::format("0x{:0X}", e.op2.index_or_imm.value());
|
||||
if(put_comma) {
|
||||
op += ", ";
|
||||
}
|
||||
op += op2;
|
||||
}
|
||||
}
|
||||
|
||||
op += '\n';
|
||||
return formatter<string_view>::format(op, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
Entry::Entry(Opcode op, Operand dst, Operand op1, Operand op2)
|
||||
: op(op), dst(dst), op1(op1), op2(op2) {}
|
||||
|
||||
Entry::Entry(Opcode op, Operand op1, Operand op2)
|
||||
: op(op), op1(op1), op2(op2) {}
|
||||
|
||||
Entry::Entry(Opcode op, Operand bOffs, Operand op1, std::optional<BranchCond> bc, Operand op2)
|
||||
: op(op), bOffs(bOffs), op1(op1), branchCond(bc), op2(op2) {}
|
||||
|
||||
Entry::Entry(Opcode op, Operand bOffs)
|
||||
: op(op), bOffs(bOffs) {}
|
||||
|
||||
Entry::Entry(Opcode op, Operand dst, Operand op1, Operand op2, Shift s)
|
||||
: op(op), dst(dst), op1(op1), op2(op2), shift(s) {}
|
||||
|
||||
void IR::push(const Entry& e) {
|
||||
code.push_back(e);
|
||||
}
|
||||
|
||||
std::vector<Entry> IR::constant_propagation(std::vector<Entry>& code_) {
|
||||
std::vector<Entry> optimized{};
|
||||
|
||||
return optimized;
|
||||
}
|
||||
|
||||
std::vector<Entry> IR::dead_code_elimination(std::vector<Entry>& code_) {
|
||||
std::vector<Entry> optimized{};
|
||||
for(const auto& i : code_) {
|
||||
bool isOp1Reg = i.op1.isReg();
|
||||
bool isOp2Reg = i.op2.isReg();
|
||||
bool isDstReg = i.dst.isReg();
|
||||
|
||||
// check for operations like "add rx, rx, 0" or "add r0, anything"
|
||||
if(isDstReg) {
|
||||
bool isDstR0 = i.dst.isReg() && i.dst.index_or_imm.has_value() && i.dst.index_or_imm.value() == 0;
|
||||
bool areDstAndOp1Same = i.dst.isReg() && i.op1.isReg() && i.dst.index_or_imm.has_value() && i.op1.index_or_imm.has_value() && i.op1.index_or_imm.value() == i.dst.index_or_imm.value();
|
||||
bool areDstAndOp2Same = i.dst.isReg() && i.op2.isReg() && i.dst.index_or_imm.has_value() && i.op2.index_or_imm.has_value() && i.op2.index_or_imm.value() == i.dst.index_or_imm.value();
|
||||
if (isDstR0) continue;
|
||||
if (i.canDoDCE()) {
|
||||
if (areDstAndOp1Same) {
|
||||
if (i.op2.isImm() && i.op2.index_or_imm.value() == 0) continue;
|
||||
}
|
||||
|
||||
if (areDstAndOp2Same) {
|
||||
if (i.op1.isImm() && i.op1.index_or_imm.value() == 0) continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
optimized.push_back(i);
|
||||
}
|
||||
|
||||
return optimized;
|
||||
}
|
||||
|
||||
void IR::optimize() {
|
||||
std::vector<Entry> optimized{};
|
||||
|
||||
while (optimized.size() < code.size()) {
|
||||
optimized = dead_code_elimination(code);
|
||||
//optimized = constant_propagation(optimized);
|
||||
code = optimized;
|
||||
}
|
||||
}
|
||||
|
||||
void IR::print() {
|
||||
for(auto e : code) {
|
||||
fmt::print("{}", e);
|
||||
}
|
||||
}
|
||||
|
||||
auto IR::begin() {
|
||||
return code.begin();
|
||||
}
|
||||
|
||||
auto IR::end() {
|
||||
return code.end();
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
#pragma once
|
||||
#include <common.hpp>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <IR/Opcode.hpp>
|
||||
|
||||
namespace n64 {
|
||||
struct Entry {
|
||||
enum : u16 {
|
||||
LINK = 0x100,
|
||||
LIKELY = 0x200,
|
||||
REGISTER = 0x400,
|
||||
SET_LLBIT = 0x800,
|
||||
UNSET_LLBIT = 0x1000,
|
||||
};
|
||||
|
||||
enum Shift {
|
||||
LEFT, RIGHT
|
||||
};
|
||||
|
||||
enum Opcode : u16 {
|
||||
MOV, SLT, ADD, SUB, UMUL, SMUL, DIV, AND, NOR,
|
||||
XOR, OR, SRL, SLL, SRA,
|
||||
LOADS8, LOADS8_SHIFT, STORE8, STORE8_SHIFT,
|
||||
LOADS16, LOADS16_SHIFT, STORE16, STORE16_SHIFT,
|
||||
LOADS32, LOADS32_SHIFT, STORE32, STORE32_SHIFT,
|
||||
LOADS64, LOADS64_SHIFT, STORE64, STORE64_SHIFT,
|
||||
LOADU8, LOADU8_SHIFT,
|
||||
LOADU16, LOADU16_SHIFT,
|
||||
LOADU32, LOADU32_SHIFT,
|
||||
LOADU64, LOADU64_SHIFT,
|
||||
BRANCH, JUMP, MTC0, MFC0
|
||||
} op;
|
||||
|
||||
struct Operand {
|
||||
enum Type {
|
||||
NONE, REG_F64, REG_F32, IMM_F64, IMM_F32,
|
||||
REG_S64, REG_S32, REG_U64, REG_U32, REG_U5, IMM_S16,
|
||||
IMM_S32, IMM_S64, IMM_U16, IMM_U32, IMM_U64, IMM_U5,
|
||||
MEM_U8, MEM_U16, MEM_U32, MEM_U64, PC64, NEXTPC64,
|
||||
LO, HI
|
||||
} type = NONE;
|
||||
|
||||
bool isReg() const {
|
||||
return type == REG_S64 || type == REG_F32 || type == REG_F64 || type == REG_S32
|
||||
|| type == REG_U64 || type == REG_U32 || type == REG_U5;
|
||||
}
|
||||
|
||||
bool isImm() const {
|
||||
return type == IMM_S64 || type == IMM_U16 || type == IMM_S16 ||
|
||||
type == IMM_F32 || type == IMM_F64 || type == IMM_S32 ||
|
||||
type == IMM_U64 || type == IMM_U32 || type == IMM_U5;
|
||||
}
|
||||
|
||||
bool isMem() const {
|
||||
return type == MEM_U8 || type == MEM_U16 || type == MEM_U32 || type == MEM_U64;
|
||||
}
|
||||
|
||||
std::optional<u64> index_or_imm = std::nullopt;
|
||||
|
||||
Operand() = default;
|
||||
Operand(Type t, std::optional <u64> imm = std::nullopt)
|
||||
: type(t), index_or_imm(imm) {}
|
||||
} dst, op1, op2;
|
||||
|
||||
bool canDoDCE() const {
|
||||
return op == ADD || op == OR || op == SRL || op == SLL || op == SRA;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Operand& GetDst() const { return dst; }
|
||||
|
||||
enum BranchCond {
|
||||
AL, EQ, NE, LT, GT, LE, GE
|
||||
};
|
||||
|
||||
std::optional<BranchCond> branchCond = std::nullopt;
|
||||
std::optional<Shift> shift = std::nullopt;
|
||||
Operand bOffs = Operand::NONE;
|
||||
|
||||
Entry(Opcode op, Operand dst, Operand op1, Operand op2);
|
||||
Entry(Opcode op, Operand op1, Operand op2);
|
||||
Entry(Opcode op, Operand bOffs, Operand op1, std::optional<BranchCond> bc, Operand op2);
|
||||
Entry(Opcode op, Operand bOffs);
|
||||
Entry(Opcode op, Operand dst, Operand op1, Operand op2, Shift s);
|
||||
};
|
||||
|
||||
struct IR {
|
||||
void push(const Entry&);
|
||||
auto begin();
|
||||
auto end();
|
||||
void print();
|
||||
void optimize();
|
||||
private:
|
||||
std::vector<Entry> constant_propagation(std::vector<Entry>&);
|
||||
std::vector<Entry> dead_code_elimination(std::vector<Entry>&);
|
||||
std::vector<Entry> code{};
|
||||
};
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
#pragma once
|
||||
#include <log.hpp>
|
||||
#include <IR/Value.hpp>
|
||||
#include <IR/Register.hpp>
|
||||
|
||||
namespace n64 {
|
||||
enum class IROpcodeClass {
|
||||
StorePC, Add, Special, Regimm, COP0, COP1
|
||||
};
|
||||
|
||||
struct IROpcode {
|
||||
virtual ~IROpcode() = default;
|
||||
virtual auto GetClass() const -> IROpcodeClass = 0;
|
||||
virtual auto Reads (IRVariable const& var) -> bool = 0;
|
||||
virtual auto Writes(IRVariable const& var) -> bool = 0;
|
||||
virtual void Repoint(IRVariable const& var_old, IRVariable const& var_new) = 0;
|
||||
virtual void PropagateConstant(IRVariable const& var, IRConstant const& constant) {}
|
||||
virtual auto ToString() -> std::string = 0;
|
||||
};
|
||||
|
||||
template<IROpcodeClass _class>
|
||||
struct IROpcodeBase : IROpcode {
|
||||
auto GetClass() const -> IROpcodeClass override { return _class; }
|
||||
};
|
||||
|
||||
template<IROpcodeClass _class>
|
||||
struct IRBinaryOpBase : IROpcodeBase<_class> {
|
||||
IRBinaryOpBase(IRVariable const& result, IRVariable lhs, IRAnyRef rhs)
|
||||
: result(const_cast<IRVariable &>(result)), lhs(lhs), rhs(rhs) {}
|
||||
|
||||
IRVariable& result;
|
||||
IRVarRef lhs;
|
||||
IRAnyRef rhs;
|
||||
|
||||
auto Reads(IRVariable const& var) -> bool override {
|
||||
return &lhs.Get() == &var ||
|
||||
(rhs.IsVariable() && (&rhs.GetVar() == &var));
|
||||
}
|
||||
|
||||
auto Writes(IRVariable const& var) -> bool override {
|
||||
return result.HasValue() && (&result == &var);
|
||||
}
|
||||
|
||||
void Repoint(
|
||||
IRVariable const& var_old,
|
||||
IRVariable const& var_new
|
||||
) override {
|
||||
// TODO: make this reusable?
|
||||
if (result.HasValue() && (&result == &var_old)) {
|
||||
result = var_new;
|
||||
}
|
||||
|
||||
lhs.Repoint(var_old, var_new);
|
||||
rhs.Repoint(var_old, var_new);
|
||||
}
|
||||
|
||||
void PropagateConstant(
|
||||
IRVariable const& var,
|
||||
IRConstant const& constant
|
||||
) override {
|
||||
rhs.PropagateConstant(var, constant);
|
||||
}
|
||||
};
|
||||
|
||||
struct IRStorePC final : IROpcodeBase<IROpcodeClass::StorePC> {
|
||||
IRStorePC(IRAnyRef val) : val(val) {}
|
||||
|
||||
IRAnyRef val;
|
||||
|
||||
auto Reads(IRVariable const& var) -> bool override {
|
||||
if(val.IsVariable()) {
|
||||
return &var == &val.GetVar();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Writes(IRVariable const& var) -> bool override {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Repoint(
|
||||
IRVariable const& var_old,
|
||||
IRVariable const& var_new
|
||||
) override {
|
||||
}
|
||||
|
||||
auto ToString() -> std::string override {
|
||||
return fmt::format("str_pc {}", std::to_string(val));
|
||||
}
|
||||
};
|
||||
|
||||
struct IRAdd final : IRBinaryOpBase<IROpcodeClass::Add> {
|
||||
using IRBinaryOpBase::IRBinaryOpBase;
|
||||
|
||||
auto ToString() -> std::string override {
|
||||
return fmt::format(
|
||||
"add {}, {}, {}",
|
||||
std::to_string(result),
|
||||
std::to_string(lhs),
|
||||
std::to_string(rhs)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
#include <log.hpp>
|
||||
|
||||
namespace n64 {
|
||||
struct IRGuestReg {
|
||||
IRGuestReg(u8 reg) : reg(reg) {}
|
||||
|
||||
/// The ARM general purpose register
|
||||
const u8 reg;
|
||||
};
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
#pragma once
|
||||
#include <log.hpp>
|
||||
|
||||
namespace n64 {
|
||||
enum IRPrimitive {
|
||||
Uint32, Sint32, Uint64, Sint64
|
||||
};
|
||||
|
||||
struct IRVariable {
|
||||
IRVariable(IRPrimitive type, const u32 id, char const* const label) : type(type), id(id), label(label), assigned(false) {}
|
||||
IRPrimitive type;
|
||||
u32 id;
|
||||
char const* label;
|
||||
bool assigned;
|
||||
|
||||
bool HasValue() const { return assigned; }
|
||||
bool IsNull() const { return !assigned; }
|
||||
};
|
||||
|
||||
struct IRConstant {
|
||||
IRConstant() {}
|
||||
IRConstant(IRPrimitive type, u64 value) : type(type), value(value) {}
|
||||
|
||||
u64 value = 0;
|
||||
private:
|
||||
IRPrimitive type = Uint64;
|
||||
};
|
||||
|
||||
struct IRAnyRef {
|
||||
IRAnyRef() {}
|
||||
IRAnyRef(IRVariable const& variable) : type(Type::Variable), var(&variable) {}
|
||||
IRAnyRef(IRConstant const& constant) : type(Type::Constant), constant(constant) {}
|
||||
|
||||
auto operator=(IRAnyRef const& other) -> IRAnyRef& {
|
||||
type = other.type;
|
||||
if (IsConstant()) {
|
||||
constant = other.constant;
|
||||
} else {
|
||||
var = other.var;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool IsNull() const { return type == Type::Null; }
|
||||
bool IsVariable() const { return type == Type::Variable; }
|
||||
bool IsConstant() const { return type == Type::Constant; }
|
||||
|
||||
auto GetVar() const -> IRVariable const& {
|
||||
if (!IsVariable()) {
|
||||
Util::panic("called GetVar() but value is a constant or null");
|
||||
}
|
||||
return *var;
|
||||
}
|
||||
|
||||
auto GetConst() const -> IRConstant const& {
|
||||
if (!IsConstant()) {
|
||||
Util::panic("called GetConst() but value is a variable or null");
|
||||
}
|
||||
return constant;
|
||||
}
|
||||
|
||||
void Repoint(IRVariable const& var_old, IRVariable const& var_new) {
|
||||
if (IsVariable() && (&GetVar() == &var_old)) {
|
||||
var = &var_new;
|
||||
}
|
||||
}
|
||||
|
||||
void PropagateConstant(IRVariable const& var, IRConstant const& constant) {
|
||||
if (IsVariable() && (&GetVar() == &var)) {
|
||||
type = Type::Constant;
|
||||
this->constant = constant;
|
||||
}
|
||||
}
|
||||
private:
|
||||
enum Type {
|
||||
Null, Variable, Constant
|
||||
};
|
||||
|
||||
Type type;
|
||||
|
||||
union {
|
||||
IRVariable const* var;
|
||||
IRConstant constant;
|
||||
};
|
||||
};
|
||||
|
||||
struct IRVarRef {
|
||||
IRVarRef(IRVariable const& var) : p_var(&var) {}
|
||||
|
||||
auto Get() const -> IRVariable const& {
|
||||
return *p_var;
|
||||
}
|
||||
|
||||
void Repoint(IRVariable const& var_old, IRVariable const& var_new) {
|
||||
if (&var_old == p_var) {
|
||||
p_var = &var_new;
|
||||
}
|
||||
}
|
||||
private:
|
||||
IRVariable const* p_var;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
namespace std {
|
||||
inline auto to_string(n64::IRPrimitive data_type) -> std::string {
|
||||
switch (data_type) {
|
||||
case n64::IRPrimitive::Uint32:
|
||||
return "u32";
|
||||
case n64::IRPrimitive::Sint32:
|
||||
return "s32";
|
||||
case n64::IRPrimitive::Uint64:
|
||||
return "u64";
|
||||
case n64::IRPrimitive::Sint64:
|
||||
return "s64";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
inline auto to_string(n64::IRVariable const &variable) -> std::string {
|
||||
if (variable.label) {
|
||||
return fmt::format("var{}_{}", variable.id, variable.label);
|
||||
}
|
||||
return fmt::format("var{}", variable.id);
|
||||
}
|
||||
|
||||
inline auto to_string(n64::IRConstant const &constant) -> std::string {
|
||||
return fmt::format("0x{:0X}", constant.value);
|
||||
}
|
||||
|
||||
inline auto to_string(n64::IRAnyRef const &value) -> std::string {
|
||||
if (value.IsNull()) {
|
||||
return "(null)";
|
||||
}
|
||||
if (value.IsConstant()) {
|
||||
return std::to_string(value.GetConst());
|
||||
}
|
||||
return std::to_string(value.GetVar());
|
||||
}
|
||||
|
||||
inline auto to_string(n64::IRVarRef const &variable) -> std::string {
|
||||
return std::to_string(variable.Get());
|
||||
}
|
||||
}
|
||||
@@ -1,925 +0,0 @@
|
||||
#include <core/JIT.hpp>
|
||||
|
||||
#define check_address_error(mask, vaddr) (((!regs.cop0.is_64bit_addressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
|
||||
#define check_signed_overflow(op1, op2, res) (((~((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1)
|
||||
#define check_signed_underflow(op1, op2, res) (((((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1)
|
||||
|
||||
namespace n64 {
|
||||
void JIT::add(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RD(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_U32, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_U32, u8(RT(instr)) };
|
||||
Entry e(Entry::ADD, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::addu(u32 instr) {
|
||||
add(instr);
|
||||
}
|
||||
|
||||
void JIT::addi(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_U32, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_S16, s16(instr) };
|
||||
Entry e(Entry::ADD, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::addiu(u32 instr) {
|
||||
addi(instr);
|
||||
}
|
||||
|
||||
void JIT::dadd(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RD(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_U64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_U64, u8(RT(instr)) };
|
||||
Entry e(Entry::ADD, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
Entry::Operand JIT::branch(u32 instr) {
|
||||
auto addr = Entry::Operand{ Entry::Operand::IMM_S64, u64(s64(s16(instr))) << 2 };
|
||||
return addr;
|
||||
}
|
||||
|
||||
void JIT::bltz(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_U64, 0 };
|
||||
Entry e(Entry::BRANCH, dst, op1, Entry::BranchCond::LT, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::bgez(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_U64, 0 };
|
||||
Entry e(Entry::BRANCH, dst, op1, Entry::BranchCond::GE, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::bltzl(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_U64, 0 };
|
||||
auto opc = Entry::Opcode(u16(Entry::BRANCH) | Entry::LIKELY);
|
||||
Entry e(opc, dst, op1, Entry::BranchCond::LT, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::bgezl(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_U64, 0 };
|
||||
auto opc = Entry::Opcode(u16(Entry::BRANCH) | Entry::LIKELY);
|
||||
Entry e(opc, dst, op1, Entry::BranchCond::GE, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::bltzal(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_U64, 0 };
|
||||
auto opc = Entry::Opcode(u16(Entry::BRANCH) | Entry::LINK);
|
||||
Entry e(opc, dst, op1, Entry::BranchCond::LT, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::bgezal(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_U64, 0 };
|
||||
auto opc = Entry::Opcode(u16(Entry::BRANCH) | Entry::LINK);
|
||||
Entry e(opc, dst, op1, Entry::BranchCond::GE, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::bltzall(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_U64, 0 };
|
||||
auto opc = Entry::Opcode(u16(Entry::BRANCH) | Entry::LINK | Entry::LIKELY);
|
||||
Entry e(opc, dst, op1, Entry::BranchCond::LT, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::bgezall(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_U64, 0 };
|
||||
auto opc = Entry::Opcode(u16(Entry::BRANCH) | Entry::LINK | Entry::LIKELY);
|
||||
Entry e(opc, dst, op1, Entry::BranchCond::GE, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::beq(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
Entry e(Entry::BRANCH, dst, op1, Entry::BranchCond::EQ, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::bne(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
Entry e(Entry::BRANCH, dst, op1, Entry::BranchCond::NE, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::blez(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_S64, 0 };
|
||||
Entry e(Entry::BRANCH, dst, op1, Entry::BranchCond::LE, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::bgtz(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_S64, 0 };
|
||||
Entry e(Entry::BRANCH, dst, op1, Entry::BranchCond::GT, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::beql(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto opc = Entry::Opcode(u16(Entry::BRANCH) | Entry::LIKELY);
|
||||
Entry e(opc, dst, op1, Entry::BranchCond::EQ, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::bnel(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto opc = Entry::Opcode(u16(Entry::BRANCH) | Entry::LIKELY);
|
||||
Entry e(opc, dst, op1, Entry::BranchCond::NE, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::blezl(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_S64, 0 };
|
||||
auto opc = Entry::Opcode(u16(Entry::BRANCH) | Entry::LIKELY);
|
||||
Entry e(opc, dst, op1, Entry::BranchCond::LE, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::bgtzl(u32 instr) {
|
||||
auto dst = branch(instr);
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_S64, 0 };
|
||||
auto opc = Entry::Opcode(u16(Entry::BRANCH) | Entry::LIKELY);
|
||||
Entry e(opc, dst, op1, Entry::BranchCond::GT, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::daddu(u32 instr) {
|
||||
dadd(instr);
|
||||
}
|
||||
|
||||
void JIT::daddi(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_S16, s16(instr) };
|
||||
Entry e(Entry::ADD, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::daddiu(u32 instr) {
|
||||
daddi(instr);
|
||||
}
|
||||
|
||||
void JIT::div(u32 instr) {
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S32, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S32, u8(RT(instr)) };
|
||||
Entry e(Entry::DIV, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::divu(u32 instr) {
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_U32, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_U32, u8(RT(instr)) };
|
||||
Entry e(Entry::DIV, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::ddiv(u32 instr) {
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
Entry e(Entry::DIV, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::ddivu(u32 instr) {
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_U64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_U64, u8(RT(instr)) };
|
||||
Entry e(Entry::DIV, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::lui(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::IMM_S16, u64(s16(instr)) << 16 };
|
||||
Entry e(Entry::LOADS64, dst, op1);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::lb(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U8, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr))};
|
||||
Entry e(Entry::LOADS8, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::lh(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U16, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::LOADS16, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::lw(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U32, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::LOADS32, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::ll(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U32, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto opc = Entry::Opcode(u16(Entry::LOADS64) | Entry::SET_LLBIT);
|
||||
Entry e(opc, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::lwl(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U32, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::LOADS32_SHIFT, dst, op1, op2, Entry::LEFT);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::lwr(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U32, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::LOADS32_SHIFT, dst, op1, op2, Entry::RIGHT);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::ld(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U64, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::LOADS64, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::lld(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U32, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto opc = Entry::Opcode(u16(Entry::Opcode::LOADS64) | Entry::SET_LLBIT);
|
||||
Entry e(opc, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::ldl(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U32, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::LOADS64_SHIFT, dst, op1, op2, Entry::LEFT);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::ldr(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U32, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::LOADS64_SHIFT, dst, op1, op2, Entry::RIGHT);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::lbu(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U8, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::LOADU8, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::lhu(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U16, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::LOADU16, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::lwu(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U32, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::LOADU16, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::sb(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U8, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::STORE8, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::sc(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U32, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto opc = Entry::Opcode(u16(Entry::STORE32) | Entry::SET_LLBIT);
|
||||
Entry e(opc, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::scd(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U32, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto opc = Entry::Opcode(u16(Entry::STORE64) | Entry::SET_LLBIT);
|
||||
Entry e(opc, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::sh(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U16, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::STORE16, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::sw(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U32, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::STORE32, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::sd(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U64, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::STORE64, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::sdl(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U64, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::STORE64, dst, op1, op2, Entry::LEFT);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::sdr(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U64, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::STORE64, dst, op1, op2, Entry::RIGHT);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::swl(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U32, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::STORE32, dst, op1, op2, Entry::LEFT);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::swr(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::MEM_U32, s16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::STORE32, dst, op1, op2, Entry::RIGHT);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::ori(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::IMM_U16, u16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::OR, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::or_(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RD(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::OR, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::nor(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RD(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::NOR, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::j(u32 instr) {
|
||||
auto dst = Entry::Operand{Entry::Operand::IMM_S64};
|
||||
auto op1 = Entry::Operand{Entry::Operand::PC64};
|
||||
auto op2 = Entry::Operand{Entry::Operand::IMM_S64, ~0xfffffff};
|
||||
Entry and_(Entry::AND, dst, op1, op2);
|
||||
ir.push(and_);
|
||||
op2 = Entry::Operand{Entry::Operand::IMM_S64, (instr & 0x3ffffff) << 2};
|
||||
Entry or_(Entry::OR, dst, dst, op2);
|
||||
ir.push(or_);
|
||||
Entry e(Entry::BRANCH, or_.GetDst());
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::jal(u32 instr) {
|
||||
Entry link(Entry::MOV,
|
||||
Entry::Operand{Entry::Operand::REG_S64, 31},
|
||||
Entry::Operand{Entry::Operand::NEXTPC64});
|
||||
ir.push(link);
|
||||
j(instr);
|
||||
}
|
||||
|
||||
void JIT::jalr(u32 instr) {
|
||||
auto addr = Entry::Operand{Entry::Operand::REG_U64, RS(instr)};
|
||||
Entry e(Entry::BRANCH, addr);
|
||||
Entry link(Entry::MOV,
|
||||
Entry::Operand{Entry::Operand::REG_S64, RD(instr)},
|
||||
Entry::Operand{Entry::Operand::PC64});
|
||||
ir.push(link);
|
||||
j(instr);
|
||||
}
|
||||
|
||||
void JIT::slti(u32 instr) {
|
||||
Entry e(Entry::SLT,
|
||||
{ Entry::Operand::REG_U5, RT(instr) },
|
||||
{ Entry::Operand::REG_S64, RS(instr) },
|
||||
{ Entry::Operand::IMM_S64, s64(s16(instr)) });
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::sltiu(u32 instr) {
|
||||
Entry e(Entry::SLT,
|
||||
{ Entry::Operand::REG_U5, RT(instr) },
|
||||
{ Entry::Operand::REG_U64, RS(instr) },
|
||||
{ Entry::Operand::IMM_U64, u64(s64(s16(instr))) });
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::slt(u32 instr) {
|
||||
Entry e(Entry::SLT,
|
||||
{ Entry::Operand::REG_U5, RD(instr) },
|
||||
{ Entry::Operand::REG_S64, RS(instr) },
|
||||
{ Entry::Operand::REG_S64, RT(instr) });
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::sltu(u32 instr) {
|
||||
Entry e(Entry::SLT,
|
||||
{ Entry::Operand::REG_U5, RD(instr) },
|
||||
{ Entry::Operand::REG_U64, RS(instr) },
|
||||
{ Entry::Operand::REG_U64, RT(instr) });
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::xori(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::IMM_U16, u16(instr)};
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::XOR, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::xor_(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RD(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::XOR, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::andi(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::IMM_U16, u16(instr) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::AND, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::and_(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RD(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
Entry e(Entry::AND, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::sll(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RD(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::IMM_U5, std::nullopt };
|
||||
Entry e(Entry::SLL, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::sllv(u32 instr) {
|
||||
if (RD(instr) != 0) [[likely]] {
|
||||
mov(rcx, GPR(qword, RS(instr)));
|
||||
CodeGenerator::and_(cl, 0x1F);
|
||||
mov(rax, GPR(qword, RT(instr)));
|
||||
sal(rax, cl);
|
||||
movsxd(rcx, eax);
|
||||
mov(GPR(qword, RD(instr)), rcx);
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::dsll32(u32 instr) {
|
||||
if (RD(instr) != 0) [[likely]] {
|
||||
u8 sa = ((instr >> 6) & 0x1f) + 32;
|
||||
mov(rax, GPR(qword, RT(instr)));
|
||||
sal(rax, sa);
|
||||
mov(GPR(qword, RT(instr)), rax);
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::dsll(u32 instr) {
|
||||
if (RD(instr) != 0) [[likely]] {
|
||||
u8 sa = ((instr >> 6) & 0x1f);
|
||||
mov(rax, GPR(qword, RT(instr)));
|
||||
sal(rax, sa);
|
||||
mov(GPR(qword, RT(instr)), rax);
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::dsllv(u32 instr) {
|
||||
if (RD(instr) != 0) [[likely]] {
|
||||
mov(rcx, GPR(qword, RS(instr)));
|
||||
CodeGenerator::and_(cl, 63);
|
||||
mov(rax, GPR(qword, RT(instr)));
|
||||
sal(rax, cl);
|
||||
mov(GPR(qword, RD(instr)), rax);
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::srl(u32 instr) {
|
||||
if (RD(instr) != 0) [[likely]] {
|
||||
u8 sa = ((instr >> 6) & 0x1f);
|
||||
mov(rax, GPR(qword, RT(instr)));
|
||||
CodeGenerator::shr(rax, sa);
|
||||
movsxd(rcx, eax);
|
||||
mov(GPR(qword, RD(instr)), rcx);
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::srlv(u32 instr) {
|
||||
if (RD(instr) != 0) [[likely]] {
|
||||
mov(rcx, GPR(qword, RS(instr)));
|
||||
CodeGenerator::and_(cl, 0x1F);
|
||||
mov(rax, GPR(qword, RT(instr)));
|
||||
shr(rax, cl);
|
||||
movsxd(rcx, eax);
|
||||
mov(GPR(qword, RD(instr)), rcx);
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::tgei(u32) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::tgeiu(u32) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::tlti(u32) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::tltiu(u32) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::teqi(u32) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::tnei(u32) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::tge(u32) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::tgeu(u32) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::tlt(u32) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::tltu(u32) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::teq(u32) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::tne(u32) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::dsrl(u32 instr) {
|
||||
if (RD(instr) != 0) [[likely]] {
|
||||
u8 sa = ((instr >> 6) & 0x1f);
|
||||
mov(rax, GPR(qword, RT(instr)));
|
||||
CodeGenerator::shr(rax, sa);
|
||||
mov(GPR(qword, RD(instr)), rax);
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::dsrlv(u32 instr) {
|
||||
if (RD(instr) != 0) [[likely]] {
|
||||
mov(rcx, GPR(qword, RS(instr)));
|
||||
CodeGenerator::and_(cl, 63);
|
||||
mov(rax, GPR(qword, RT(instr)));
|
||||
shr(rax, cl);
|
||||
mov(GPR(qword, RD(instr)), rax);
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::dsrl32(u32 instr) {
|
||||
if (RD(instr) != 0) [[likely]] {
|
||||
u8 sa = ((instr >> 6) & 0x1f) + 32;
|
||||
mov(rax, GPR(qword, RT(instr)));
|
||||
CodeGenerator::shr(rax, sa);
|
||||
mov(GPR(qword, RD(instr)), rax);
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::sra(u32 instr) {
|
||||
if (RD(instr) != 0) [[likely]] {
|
||||
u8 sa = ((instr >> 6) & 0x1f);
|
||||
mov(rax, GPR(qword, RT(instr)));
|
||||
sar(rax, sa);
|
||||
movsxd(rcx, eax);
|
||||
mov(GPR(qword, RD(instr)), rcx);
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::srav(u32 instr) {
|
||||
if (RD(instr) != 0) [[likely]] {
|
||||
mov(rcx, GPR(qword, RS(instr)));
|
||||
CodeGenerator::and_(cl, 0x1F);
|
||||
mov(rax, GPR(qword, RT(instr)));
|
||||
sar(rax, cl);
|
||||
movsxd(rcx, eax);
|
||||
mov(GPR(qword, RD(instr)), rcx);
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::dsra(u32 instr) {
|
||||
if (RD(instr) != 0) [[likely]] {
|
||||
u8 sa = ((instr >> 6) & 0x1f);
|
||||
mov(rax, GPR(qword, RT(instr)));
|
||||
sar(rax, sa);
|
||||
mov(GPR(qword, RD(instr)), rax);
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::dsrav(u32 instr) {
|
||||
if (RD(instr) != 0) [[likely]] {
|
||||
mov(rcx, GPR(qword, RS(instr)));
|
||||
CodeGenerator::and_(cl, 63);
|
||||
mov(rax, GPR(qword, RT(instr)));
|
||||
sar(rax, cl);
|
||||
mov(GPR(qword, RD(instr)), rax);
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::dsra32(u32 instr) {
|
||||
if (RD(instr) != 0) [[likely]] {
|
||||
u8 sa = ((instr >> 6) & 0x1f) + 32;
|
||||
mov(rax, GPR(qword, RT(instr)));
|
||||
sar(rax, sa);
|
||||
mov(GPR(qword, RD(instr)), rax);
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::jr(u32 instr) {
|
||||
auto addr = Entry::Operand{Entry::Operand::REG_U64, RS(instr)};
|
||||
Entry e(Entry::BRANCH, addr);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::dsub(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RD(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
Entry e(Entry::SUB, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::dsubu(u32 instr) {
|
||||
dsub(instr);
|
||||
}
|
||||
|
||||
void JIT::sub(u32 instr) {
|
||||
auto dst = Entry::Operand{ Entry::Operand::REG_S64, u8(RD(instr)) };
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S32, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S32, u8(RT(instr)) };
|
||||
Entry e(Entry::SUB, dst, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::subu(u32 instr) {
|
||||
sub(instr);
|
||||
}
|
||||
|
||||
void JIT::dmultu(u32 instr) {
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_U64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_U64, u8(RT(instr)) };
|
||||
Entry e(Entry::UMUL, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::dmult(u32 instr) {
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S64, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S64, u8(RT(instr)) };
|
||||
Entry e(Entry::SMUL, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::multu(u32 instr) {
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S32, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S32, u8(RT(instr)) };
|
||||
Entry e(Entry::UMUL, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::mult(u32 instr) {
|
||||
auto op1 = Entry::Operand{ Entry::Operand::REG_S32, u8(RS(instr)) };
|
||||
auto op2 = Entry::Operand{ Entry::Operand::REG_S32, u8(RT(instr)) };
|
||||
Entry e(Entry::SMUL, op1, op2);
|
||||
ir.push(e);
|
||||
}
|
||||
|
||||
void JIT::mflo(u32 instr) {
|
||||
auto dst = Entry::Operand{Entry::Operand::REG_S64, RD(instr)};
|
||||
auto src = Entry::Operand{Entry::Operand::LO};
|
||||
ir.push({Entry::MOV, dst, src});
|
||||
}
|
||||
|
||||
void JIT::mfhi(u32 instr) {
|
||||
auto dst = Entry::Operand{Entry::Operand::REG_S64, RD(instr)};
|
||||
auto src = Entry::Operand{Entry::Operand::HI};
|
||||
ir.push({Entry::MOV, dst, src});
|
||||
}
|
||||
|
||||
void JIT::mtlo(u32 instr) {
|
||||
auto dst = Entry::Operand{Entry::Operand::LO};
|
||||
auto src = Entry::Operand{Entry::Operand::REG_S64, RS(instr)};
|
||||
ir.push({Entry::MOV, dst, src});
|
||||
}
|
||||
|
||||
void JIT::mthi(u32 instr) {
|
||||
auto dst = Entry::Operand{Entry::Operand::HI};
|
||||
auto src = Entry::Operand{Entry::Operand::REG_S64, RS(instr)};
|
||||
ir.push({Entry::MOV, dst, src});
|
||||
}
|
||||
|
||||
void JIT::mtc0(u32 instr) {
|
||||
ir.push({Entry::MTC0,
|
||||
{Entry::Operand::IMM_U5, RD(instr)},
|
||||
{Entry::Operand::REG_S32, RT(instr)}});
|
||||
}
|
||||
|
||||
void JIT::dmtc0(u32 instr) {
|
||||
ir.push({Entry::MTC0,
|
||||
{Entry::Operand::IMM_U5, RD(instr)},
|
||||
{Entry::Operand::REG_S64, RT(instr)}});
|
||||
}
|
||||
|
||||
void JIT::mfc0(u32 instr) {
|
||||
ir.push({Entry::MFC0,
|
||||
{Entry::Operand::REG_S32, RT(instr)},
|
||||
{Entry::Operand::IMM_U5, RD(instr)}});
|
||||
}
|
||||
|
||||
void JIT::dmfc0(u32 instr) {
|
||||
ir.push({Entry::MFC0,
|
||||
{Entry::Operand::REG_S64, RT(instr)},
|
||||
{Entry::Operand::IMM_U5, RD(instr)}});
|
||||
}
|
||||
|
||||
void JIT::eret() {
|
||||
/*if(status.erl) {
|
||||
regs.SetPC64(ErrorEPC);
|
||||
status.erl = false;
|
||||
} else {
|
||||
regs.SetPC64(EPC);
|
||||
status.exl = false;
|
||||
}
|
||||
regs.cop0.Update();
|
||||
llbit = false;*/
|
||||
}
|
||||
|
||||
|
||||
void JIT::tlbr() {
|
||||
/*if (index.i >= 32) {
|
||||
Util::panic("TLBR with TLB index {}", index.i);
|
||||
}
|
||||
|
||||
TLBEntry entry = tlb[index.i];
|
||||
|
||||
entryHi.raw = entry.entryHi.raw;
|
||||
entryLo0.raw = entry.entryLo0.raw & 0x3FFFFFFF;
|
||||
entryLo1.raw = entry.entryLo1.raw & 0x3FFFFFFF;
|
||||
|
||||
entryLo0.g = entry.global;
|
||||
entryLo1.g = entry.global;
|
||||
pageMask.raw = entry.pageMask.raw;*/
|
||||
}
|
||||
|
||||
void JIT::tlbw(int index_) {
|
||||
/*PageMask page_mask{};
|
||||
page_mask = pageMask;
|
||||
u32 top = page_mask.mask & 0xAAA;
|
||||
page_mask.mask = top | (top >> 1);
|
||||
|
||||
if(index_ >= 32) {
|
||||
Util::panic("TLBWI with TLB index {}", index_);
|
||||
}
|
||||
|
||||
tlb[index_].entryHi.raw = entryHi.raw;
|
||||
tlb[index_].entryHi.vpn2 &= ~page_mask.mask;
|
||||
|
||||
tlb[index_].entryLo0.raw = entryLo0.raw & 0x03FFFFFE;
|
||||
tlb[index_].entryLo1.raw = entryLo1.raw & 0x03FFFFFE;
|
||||
tlb[index_].pageMask.raw = page_mask.raw;
|
||||
|
||||
tlb[index_].global = entryLo0.g && entryLo1.g;
|
||||
tlb[index_].initialized = true;*/
|
||||
}
|
||||
|
||||
void JIT::tlbp() {
|
||||
/*int match = -1;
|
||||
TLBEntry* entry = TLBTryMatch(regs, entryHi.raw, &match);
|
||||
if(entry && match >= 0) {
|
||||
index.raw = match;
|
||||
} else {
|
||||
index.raw = 0;
|
||||
index.p = 1;
|
||||
}*/
|
||||
}
|
||||
|
||||
void JIT::mtc2(u32 instr) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::mfc2(u32 instr) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::dmtc2(u32 instr) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::dmfc2(u32 instr) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::ctc2(u32) {
|
||||
|
||||
}
|
||||
|
||||
void JIT::cfc2(u32) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -49,7 +49,7 @@ void Flash::CommandExecute() {
|
||||
case FlashState::Erase:
|
||||
if(saveData.is_mapped()) {
|
||||
for (int i = 0; i < 128; i++) {
|
||||
saveData[eraseOffs + i] = 0xFFi8;
|
||||
saveData[eraseOffs + i] = 0xFF;
|
||||
}
|
||||
} else {
|
||||
Util::panic("Accessing flash when not mapped!");
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <log.hpp>
|
||||
#include <core/registers/Registers.hpp>
|
||||
#include <core/Interpreter.hpp>
|
||||
#include <core/JIT.hpp>
|
||||
|
||||
namespace n64 {
|
||||
Cop0::Cop0() {
|
||||
@@ -337,25 +336,7 @@ template void Cop0::decode<Interpreter>(Interpreter&, u32);
|
||||
template void Cop0::decode<JIT>(JIT&, u32);
|
||||
|
||||
void Cop0::decodeJIT(JIT& cpu, u32 instr) {
|
||||
u8 mask_cop = (instr >> 21) & 0x1F;
|
||||
u8 mask_cop2 = instr & 0x3F;
|
||||
switch(mask_cop) {
|
||||
case 0x00: cpu.mfc0(instr); break;
|
||||
case 0x01: cpu.dmfc0(instr); break;
|
||||
case 0x04: cpu.mtc0(instr); break;
|
||||
case 0x05: cpu.dmtc0(instr); break;
|
||||
case 0x10 ... 0x1F:
|
||||
switch(mask_cop2) {
|
||||
case 0x01: cpu.tlbr(); break;
|
||||
case 0x02: cpu.tlbw(index.i); break;
|
||||
case 0x06: cpu.tlbw(GetRandom()); break;
|
||||
case 0x08: cpu.tlbp(); break;
|
||||
case 0x18: cpu.eret(); break;
|
||||
default: Util::panic("Unimplemented COP0 function {} {} ({:08X}) ({:016lX})", mask_cop2 >> 3, mask_cop2 & 7, instr, cpu.regs.oldPC);
|
||||
}
|
||||
break;
|
||||
default: Util::panic("Unimplemented COP0 instruction {} {}", mask_cop >> 4, mask_cop & 7);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Cop0::decodeInterp(Registers& regs, u32 instr) {
|
||||
|
||||
Reference in New Issue
Block a user