# Conflicts:
#	CMakeLists.txt
#	src/backend/core/JIT.cpp
#	src/backend/core/JIT.hpp
#	src/backend/core/JIT/instructions.cpp
#	src/backend/core/mmio/PIF/Device.cpp
#	src/frontend/imgui/Settings.hpp
This commit is contained in:
Simone
2024-01-23 14:19:52 +01:00
12 changed files with 1911 additions and 4 deletions

105
src/backend/core/JIT.cpp Normal file
View File

@@ -0,0 +1,105 @@
#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;
}
}

259
src/backend/core/JIT.hpp Normal file
View File

@@ -0,0 +1,259 @@
#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)&regs.gpr[(x)] - (uintptr_t)this)
#define REG_OFFSET(member) ((uintptr_t)&regs.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);
};
}

241
src/backend/core/JIT/IR.cpp Normal file
View File

@@ -0,0 +1,241 @@
#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();
}
}

View File

@@ -0,0 +1,98 @@
#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{};
};
}

View File

@@ -0,0 +1,104 @@
#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)
);
}
};
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include <log.hpp>
namespace n64 {
struct IRGuestReg {
IRGuestReg(u8 reg) : reg(reg) {}
/// The ARM general purpose register
const u8 reg;
};
}

View File

@@ -0,0 +1,145 @@
#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());
}
}

View File

@@ -0,0 +1,925 @@
#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) {
}
}

View File

@@ -49,7 +49,7 @@ void Flash::CommandExecute() {
case FlashState::Erase:
if(saveData.is_mapped()) {
for (int i = 0; i < 128; i++) {
saveData[eraseOffs + i] = 0xFF;
saveData[eraseOffs + i] = 0xFFi8;
}
} else {
Util::panic("Accessing flash when not mapped!");

View File

@@ -1,6 +1,7 @@
#include <log.hpp>
#include <core/registers/Registers.hpp>
#include <core/Interpreter.hpp>
#include <core/JIT.hpp>
namespace n64 {
Cop0::Cop0() {
@@ -336,7 +337,25 @@ 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) {