From ec6db575fab24de7da25498ab84b0c2f86b5dfb4 Mon Sep 17 00:00:00 2001 From: Bashar Astifan Date: Thu, 17 Aug 2023 18:45:58 +0400 Subject: [PATCH 01/36] Update OSVersion.cpp --- Common/OSVersion.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Common/OSVersion.cpp b/Common/OSVersion.cpp index ba35239cb7..f50c171094 100644 --- a/Common/OSVersion.cpp +++ b/Common/OSVersion.cpp @@ -13,11 +13,11 @@ struct WindowsReleaseInfo { - int major; - int minor; - int spMajor; - int spMinor; - int build; + uint32_t major; + uint32_t minor; + uint32_t spMajor; + uint32_t spMinor; + uint32_t build; bool greater = false; }; @@ -96,9 +96,9 @@ bool DoesVersionMatchWindows(uint32_t major, uint32_t minor, uint32_t spMajor, u bool DoesVersionMatchWindows(WindowsReleaseInfo release) { if (release.spMajor == 0 && release.spMinor == 0) { // Compare Info - int major = release.major; - int minor = release.minor; - int build = release.build; + uint32_t major = release.major; + uint32_t minor = release.minor; + uint32_t build = release.build; bool greater = release.greater; OSVERSIONINFOEX osvi; @@ -124,7 +124,11 @@ bool DoesVersionMatchWindows(WindowsReleaseInfo release) { } } } - return DoesVersionMatchWindows(release.major, release.minor, release.spMajor, release.spMinor, release.greater); + else { + return DoesVersionMatchWindows(release.major, release.minor, release.spMajor, release.spMinor, release.greater); + } + + return false; } bool IsVistaOrHigher() { From 57123e8f9e17cf96995a644fdebaf6abbce016c0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 19 Aug 2023 22:36:35 -0700 Subject: [PATCH 02/36] irjit: Reserve some arrays that churn. Improves IR compile time by around 20-30%. --- Core/MIPS/IR/IRInst.h | 3 +++ Core/MIPS/IR/IRJit.cpp | 1 + Core/MIPS/IR/IRPassSimplify.cpp | 9 +++++++++ 3 files changed, 13 insertions(+) diff --git a/Core/MIPS/IR/IRInst.h b/Core/MIPS/IR/IRInst.h index 2b192a6890..f9131152af 100644 --- a/Core/MIPS/IR/IRInst.h +++ b/Core/MIPS/IR/IRInst.h @@ -375,6 +375,9 @@ public: int AddConstant(u32 value); int AddConstantFloat(float value); + void Reserve(size_t s) { + insts_.reserve(s); + } void Clear() { insts_.clear(); } diff --git a/Core/MIPS/IR/IRJit.cpp b/Core/MIPS/IR/IRJit.cpp index 44621c2dd5..e64dcf3ecd 100644 --- a/Core/MIPS/IR/IRJit.cpp +++ b/Core/MIPS/IR/IRJit.cpp @@ -168,6 +168,7 @@ void IRJit::CompileFunction(u32 start_address, u32 length) { // We may go up and down from branches, so track all block starts done here. std::set doneAddresses; std::vector pendingAddresses; + pendingAddresses.reserve(16); pendingAddresses.push_back(start_address); while (!pendingAddresses.empty()) { u32 em_address = pendingAddresses.back(); diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index 3c020edd4f..8f9485c11d 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -86,6 +86,8 @@ IROp ShiftToShiftImm(IROp op) { } bool IRApplyPasses(const IRPassFunc *passes, size_t c, const IRWriter &in, IRWriter &out, const IROptions &opts) { + out.Reserve(in.GetInstructions().size()); + if (c == 1) { return passes[0](in, out, opts); } @@ -95,6 +97,7 @@ bool IRApplyPasses(const IRPassFunc *passes, size_t c, const IRWriter &in, IRWri IRWriter temp[2]; const IRWriter *nextIn = ∈ IRWriter *nextOut = &temp[1]; + temp[1].Reserve(nextIn->GetInstructions().size()); for (size_t i = 0; i < c - 1; ++i) { if (passes[i](*nextIn, *nextOut, opts)) { logBlocks = true; @@ -102,8 +105,12 @@ bool IRApplyPasses(const IRPassFunc *passes, size_t c, const IRWriter &in, IRWri temp[0] = std::move(temp[1]); nextIn = &temp[0]; + + temp[1].Clear(); + temp[1].Reserve(nextIn->GetInstructions().size()); } + out.Reserve(nextIn->GetInstructions().size()); if (passes[c - 1](*nextIn, out, opts)) { logBlocks = true; } @@ -947,6 +954,8 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) { int8_t fplen = 0; }; std::vector checks; + checks.reserve(insts.size() / 2); + // This tracks the last index at which each reg was modified. int lastWrittenTo[256]; int lastReadFrom[256]; From 552cd88938c4b36af9443db981bd7dec8fb6c163 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 19 Aug 2023 23:47:19 -0700 Subject: [PATCH 03/36] irjit: Skip some work in PurgeTemps. --- Core/MIPS/IR/IRAnalysis.cpp | 25 +++++++++++------- Core/MIPS/IR/IRAnalysis.h | 4 +-- Core/MIPS/IR/IRPassSimplify.cpp | 47 ++++++++++++++++++++++----------- 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/Core/MIPS/IR/IRAnalysis.cpp b/Core/MIPS/IR/IRAnalysis.cpp index 2de3adb8d1..22bdada374 100644 --- a/Core/MIPS/IR/IRAnalysis.cpp +++ b/Core/MIPS/IR/IRAnalysis.cpp @@ -21,28 +21,35 @@ #include -static bool IRReadsFrom(const IRInst &inst, int reg, char type, bool directly = false) { +static bool IRReadsFrom(const IRInst &inst, int reg, char type, bool *directly) { const IRMeta *m = GetIRMeta(inst.op); if (m->types[1] == type && inst.src1 == reg) { + if (directly) + *directly = true; return true; } if (m->types[2] == type && inst.src2 == reg) { + if (directly) + *directly = true; return true; } if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && m->types[0] == type && inst.src3 == reg) { + if (directly) + *directly = true; return true; } - if (!directly) { - if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement || inst.op == IROp::Syscall || inst.op == IROp::Break) - return true; - if (inst.op == IROp::Breakpoint || inst.op == IROp::MemoryCheck) - return true; - } + + if (directly) + *directly = false; + if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement || inst.op == IROp::Syscall || inst.op == IROp::Break) + return true; + if (inst.op == IROp::Breakpoint || inst.op == IROp::MemoryCheck) + return true; return false; } -bool IRReadsFromFPR(const IRInst &inst, int reg, bool directly) { +bool IRReadsFromFPR(const IRInst &inst, int reg, bool *directly) { if (IRReadsFrom(inst, reg, 'F', directly)) return true; @@ -85,7 +92,7 @@ static int IRReadsFromList(const IRInst &inst, IRReg regs[4], char type) { return c; } -bool IRReadsFromGPR(const IRInst &inst, int reg, bool directly) { +bool IRReadsFromGPR(const IRInst &inst, int reg, bool *directly) { return IRReadsFrom(inst, reg, 'G', directly); } diff --git a/Core/MIPS/IR/IRAnalysis.h b/Core/MIPS/IR/IRAnalysis.h index 90650f4360..9853a33762 100644 --- a/Core/MIPS/IR/IRAnalysis.h +++ b/Core/MIPS/IR/IRAnalysis.h @@ -19,8 +19,8 @@ #include "Core/MIPS/IR/IRInst.h" -bool IRReadsFromFPR(const IRInst &inst, int reg, bool directly = false); -bool IRReadsFromGPR(const IRInst &inst, int reg, bool directly = false); +bool IRReadsFromFPR(const IRInst &inst, int reg, bool *directly = nullptr); +bool IRReadsFromGPR(const IRInst &inst, int reg, bool *directly = nullptr); bool IRWritesToGPR(const IRInst &inst, int reg); bool IRWritesToFPR(const IRInst &inst, int reg); int IRDestGPR(const IRInst &inst); diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index 8f9485c11d..323b7ae2f9 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -962,40 +962,57 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) { memset(lastWrittenTo, -1, sizeof(lastWrittenTo)); memset(lastReadFrom, -1, sizeof(lastReadFrom)); - auto readsFromFPRCheck = [](IRInst &inst, Check &check, bool directly) { + auto readsFromFPRCheck = [](IRInst &inst, Check &check, bool *directly) { if (check.reg < 32) return false; - if (check.fplen >= 1 && IRReadsFromFPR(inst, check.reg - 32, directly)) - return true; - if (check.fplen >= 2 && IRReadsFromFPR(inst, check.reg - 32 + 1, directly)) - return true; - if (check.fplen >= 3 && IRReadsFromFPR(inst, check.reg - 32 + 2, directly)) - return true; - if (check.fplen >= 4 && IRReadsFromFPR(inst, check.reg - 32 + 3, directly)) - return true; - return false; + + bool result = false; + *directly = true; + for (int i = 0; i < 4; ++i) { + bool laneDirectly; + if (check.fplen >= i + 1 && IRReadsFromFPR(inst, check.reg - 32 + i, &laneDirectly)) { + result = true; + if (!laneDirectly) { + *directly = false; + break; + } + } + } + return result; }; bool logBlocks = false; + size_t firstCheck = 0; for (int i = 0, n = (int)in.GetInstructions().size(); i < n; i++) { IRInst inst = in.GetInstructions()[i]; const IRMeta *m = GetIRMeta(inst.op); + // It helps to skip through rechecking ones we already discarded. + for (size_t ch = firstCheck; ch < checks.size(); ++ch) { + Check &check = checks[ch]; + if (check.reg != 0) { + firstCheck = ch; + break; + } + } + // Check if we can optimize by running through all the writes we've previously found. - for (Check &check : checks) { + for (size_t ch = firstCheck; ch < checks.size(); ++ch) { + Check &check = checks[ch]; if (check.reg == 0) { // This means we already optimized this or a later inst depends on it. continue; } - if (IRReadsFromGPR(inst, check.reg)) { + bool readsDirectly; + if (IRReadsFromGPR(inst, check.reg, &readsDirectly)) { // If this reads from the reg, we either depend on it or we can fold or swap. // That's determined below. // If this reads and writes the reg (e.g. MovZ, Load32Left), we can't just swap. bool mutatesReg = IRMutatesDestGPR(inst, check.reg); // If this doesn't directly read (i.e. Interpret), we can't swap. - bool cannotReplace = !IRReadsFromGPR(inst, check.reg, true); + bool cannotReplace = !readsDirectly; if (!mutatesReg && !cannotReplace && check.srcReg >= 0 && lastWrittenTo[check.srcReg] < check.index) { // Replace with the srcReg instead. This happens with non-nice delay slots. // We're changing "Mov A, B; Add C, C, A" to "Mov A, B; Add C, C, B" here. @@ -1027,7 +1044,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) { // Legitimately read from, so we can't optimize out. check.reg = 0; } - } else if (readsFromFPRCheck(inst, check, false) && check.fplen >= 1) { + } else if (check.fplen >= 1 && readsFromFPRCheck(inst, check, &readsDirectly)) { // If one or the other is a Vec, they must match. bool lenMismatch = false; @@ -1052,7 +1069,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) { if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0) checkMismatch(inst.src3, m->types[3]); - bool cannotReplace = !readsFromFPRCheck(inst, check, true) || lenMismatch; + bool cannotReplace = !readsDirectly || lenMismatch; if (!cannotReplace && check.srcReg >= 32 && lastWrittenTo[check.srcReg] < check.index) { // This is probably not worth doing unless we can get rid of a temp. if (!check.readByExit) { From 32d8f6196f6c265b1dee77ec10b88fd9d7683319 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 19 Aug 2023 23:48:00 -0700 Subject: [PATCH 04/36] irjit: Cut time flushing imm regs. --- Core/MIPS/IR/IRRegCache.cpp | 31 ++++++++++++++++++++----------- Core/MIPS/IR/IRRegCache.h | 16 ++++++---------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index f3b45bef71..b2851b9a9d 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -30,13 +30,13 @@ #include "Core/MIPS/JitCommon/JitState.h" void IRImmRegCache::Flush(IRReg rd) { - if (rd == 0) { - return; - } - if (reg_[rd].isImm) { + if (isImm_[rd]) { + if (rd == 0) { + return; + } _assert_((rd > 0 && rd < 32) || (rd >= IRTEMP_0 && rd < IRREG_VFPU_CTRL_BASE)); - ir_->WriteSetConstant(rd, reg_[rd].immVal); - reg_[rd].isImm = false; + ir_->WriteSetConstant(rd, immVal_[rd]); + isImm_[rd] = false; } } @@ -44,18 +44,27 @@ void IRImmRegCache::Discard(IRReg rd) { if (rd == 0) { return; } - reg_[rd].isImm = false; + isImm_[rd] = false; } IRImmRegCache::IRImmRegCache(IRWriter *ir) : ir_(ir) { - memset(®_, 0, sizeof(reg_)); - reg_[0].isImm = true; + memset(&isImm_, 0, sizeof(isImm_)); + memset(&immVal_, 0, sizeof(immVal_)); + isImm_[0] = true; ir_ = ir; } void IRImmRegCache::FlushAll() { - for (int i = 0; i < TOTAL_MAPPABLE_IRREGS; i++) { - Flush(i); + for (int i = 1; i < TOTAL_MAPPABLE_IRREGS; ) { + if (isImm_[i]) { + Flush(i); + } + + // Most of the time, lots are not. This speeds it up a lot. + bool *next = (bool *)memchr(&isImm_[i], 1, TOTAL_MAPPABLE_IRREGS - i); + if (!next) + break; + i = (int)(next - &isImm_[0]); } } diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index 33779b701a..86b704daa2 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -48,12 +48,12 @@ public: IRImmRegCache(IRWriter *ir); void SetImm(IRReg r, u32 immVal) { - reg_[r].isImm = true; - reg_[r].immVal = immVal; + isImm_[r] = true; + immVal_[r] = immVal; } - bool IsImm(IRReg r) const { return reg_[r].isImm; } - u32 GetImm(IRReg r) const { return reg_[r].immVal; } + bool IsImm(IRReg r) const { return isImm_[r]; } + u32 GetImm(IRReg r) const { return immVal_[r]; } void FlushAll(); @@ -68,12 +68,8 @@ private: void Flush(IRReg rd); void Discard(IRReg rd); - struct RegIR { - bool isImm; - u32 immVal; - }; - - RegIR reg_[TOTAL_MAPPABLE_IRREGS]; + bool isImm_[TOTAL_MAPPABLE_IRREGS]; + uint32_t immVal_[TOTAL_MAPPABLE_IRREGS]; IRWriter *ir_; }; From a23ade8f75549f2cd4346fcace218eeb95970735 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 00:04:37 -0700 Subject: [PATCH 05/36] riscv: Map IR regs based on metadata. Only doing this in places without GPR/FPR mix or FPR/Vec overlap for now. --- Core/MIPS/IR/IRRegCache.cpp | 115 +++++++++++++++++++- Core/MIPS/IR/IRRegCache.h | 8 +- Core/MIPS/RiscV/RiscVCompALU.cpp | 141 +++++++++++++------------ Core/MIPS/RiscV/RiscVCompBranch.cpp | 6 +- Core/MIPS/RiscV/RiscVCompFPU.cpp | 102 +++++++++--------- Core/MIPS/RiscV/RiscVCompLoadStore.cpp | 32 +++--- Core/MIPS/RiscV/RiscVCompSystem.cpp | 16 +-- Core/MIPS/RiscV/RiscVCompVec.cpp | 47 ++++----- Core/MIPS/RiscV/RiscVRegCache.cpp | 50 ++------- Core/MIPS/RiscV/RiscVRegCache.h | 10 +- Core/MIPS/RiscV/RiscVRegCacheFPU.cpp | 54 +++------- Core/MIPS/RiscV/RiscVRegCacheFPU.h | 5 +- 12 files changed, 319 insertions(+), 267 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index f3b45bef71..9f45e1e7b2 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -102,7 +102,7 @@ void IRNativeRegCacheBase::Start(MIPSComp::IRBlock *irBlock) { initialReady_ = true; } - memcpy(nr, nrInitial_, sizeof(nr[0]) * totalNativeRegs_); + memcpy(nr, nrInitial_, sizeof(nr[0]) * config_.totalNativeRegs); memcpy(mr, mrInitial_, sizeof(mr)); int numStatics; @@ -123,7 +123,7 @@ void IRNativeRegCacheBase::Start(MIPSComp::IRBlock *irBlock) { } void IRNativeRegCacheBase::SetupInitialRegs() { - _assert_msg_(totalNativeRegs_ > 0, "totalNativeRegs_ was never set by backend"); + _assert_msg_(config_.totalNativeRegs > 0, "totalNativeRegs was never set by backend"); // Everything else is initialized in the struct. mrInitial_[MIPS_REG_ZERO].loc = MIPSLoc::IMM; @@ -421,7 +421,7 @@ IRNativeReg IRNativeRegCacheBase::FindBestToSpill(MIPSLoc type, bool unusedOnly, } void IRNativeRegCacheBase::DiscardNativeReg(IRNativeReg nreg) { - _assert_msg_(nreg >= 0 && nreg < totalNativeRegs_, "DiscardNativeReg on invalid register %d", nreg); + _assert_msg_(nreg >= 0 && nreg < config_.totalNativeRegs, "DiscardNativeReg on invalid register %d", nreg); if (nr[nreg].mipsReg != IRREG_INVALID) { _assert_(nr[nreg].mipsReg != MIPS_REG_ZERO); int8_t lanes = 0; @@ -460,7 +460,7 @@ void IRNativeRegCacheBase::DiscardNativeReg(IRNativeReg nreg) { } void IRNativeRegCacheBase::FlushNativeReg(IRNativeReg nreg) { - _assert_msg_(nreg >= 0 && nreg < totalNativeRegs_, "FlushNativeReg on invalid register %d", nreg); + _assert_msg_(nreg >= 0 && nreg < config_.totalNativeRegs, "FlushNativeReg on invalid register %d", nreg); if (nr[nreg].mipsReg == IRREG_INVALID || nr[nreg].mipsReg == MIPS_REG_ZERO) { // Nothing to do, reg not mapped or mapped to fixed zero. _dbg_assert_(!nr[nreg].isDirty); @@ -614,13 +614,118 @@ void IRNativeRegCacheBase::FlushAll() { } } // Sanity check - for (int i = 0; i < totalNativeRegs_; i++) { + for (int i = 0; i < config_.totalNativeRegs; i++) { if (nr[i].mipsReg != IRREG_INVALID && !mr[nr[i].mipsReg].isStatic) { ERROR_LOG_REPORT(JIT, "Flush fail: nr[%i].mipsReg=%i", i, nr[i].mipsReg); } } } +void IRNativeRegCacheBase::Map(const IRInst &inst) { + const IRMeta *m = GetIRMeta(inst.op); + IRReg regs[3]{ inst.dest, inst.src1, inst.src2 }; + MIPSLoc regTypes[3]{ MIPSLoc::MEM, MIPSLoc::MEM, MIPSLoc::MEM }; + int regLanes[3]{ 1, 1, 1 }; + for (int i = 0; i < 3; ++i) { + switch (m->types[i]) { + case 'G': + regTypes[i] = MIPSLoc::REG; + _assert_msg_(IsValidGPR(regs[i]), "G was not valid GPR?"); + break; + + case 'F': + regs[i] += 32; + regTypes[i] = MIPSLoc::FREG; + _assert_msg_(IsValidFPR(regs[i] - 32), "F was not valid FPR?"); + break; + + case 'V': + case '2': + regs[i] += 32; + regTypes[i] = config_.mapUseVRegs ? MIPSLoc::VREG : MIPSLoc::FREG; + regLanes[i] = m->types[i] == 'V' ? 4 : (m->types[i] == '2' ? 2 : 1); + _assert_msg_(IsValidFPR(regs[i] - 32), "%c was not valid FPR?", m->types[i]); + break; + + case 'T': + regTypes[i] = MIPSLoc::REG; + _assert_msg_(regs[i] < VFPU_CTRL_MAX, "T was not valid VFPU CTRL?"); + regs[i] += IRREG_VFPU_CTRL_BASE; + break; + + case '\0': + case '_': + case 'C': + case 'I': + case 'v': + case 's': + case 'm': + regs[i] = IRREG_INVALID; + regLanes[i] = 0; + break; + + default: + _assert_msg_(regs[i] == IRREG_INVALID, "Unexpected register type %c", m->types[i]); + break; + } + } + + SetSpillLockIRIndex(regs[0], regs[1], regs[2], IRREG_INVALID, 0, irIndex_); + for (int i = 0; i < 3 && !config_.mapSIMD; ++i) { + for (int j = 1; j < regLanes[i]; ++j) + SetSpillLockIRIndex(regs[i] + j, IRREG_INVALID, IRREG_INVALID, IRREG_INVALID, 0, irIndex_); + } + + auto mapRegs = [&](int i, MIPSMap flags) { + if (config_.mapSIMD) { + MapNativeReg(regTypes[i], regs[i], regLanes[i], flags); + return; + } + + for (int j = 0; j < regLanes[i]; ++j) + MapNativeReg(regTypes[i], regs[i] + j, 1, flags); + }; + + if (regs[1] != IRREG_INVALID) + mapRegs(1, MIPSMap::INIT); + if (regs[2] != IRREG_INVALID) + mapRegs(2, MIPSMap::INIT); + if (regs[0] != IRREG_INVALID) { + MIPSMap flags = MIPSMap::NOINIT; + if ((m->flags & IRFLAG_SRC3DST) != 0) + flags = MIPSMap::DIRTY; + else if ((m->flags & IRFLAG_SRC3) != 0) + flags = MIPSMap::INIT; + + mapRegs(0, flags); + } + + SetSpillLockIRIndex(regs[0], regs[1], regs[2], IRREG_INVALID, 0, -1); + for (int i = 0; i < 3 && !config_.mapSIMD; ++i) { + for (int j = 1; j < regLanes[i]; ++j) + SetSpillLockIRIndex(regs[i] + j, IRREG_INVALID, IRREG_INVALID, IRREG_INVALID, 0, -1); + } + + // Sanity check. If these don't pass, we may have Vec overlap issues or etc. + for (int i = 0; i < 3; ++i) { + if (regs[i] != IRREG_INVALID) { + _dbg_assert_(mr[regs[i]].nReg != -1); + if (regTypes[i] == MIPSLoc::REG) { + _dbg_assert_(mr[regs[i]].loc == MIPSLoc::REG || mr[regs[i]].loc == MIPSLoc::REG_AS_PTR || mr[regs[i]].loc == MIPSLoc::REG_IMM); + } else { + _dbg_assert_(mr[regs[i]].loc == regTypes[i]); + } + if (regLanes[i] != 1 && config_.mapSIMD) { + _dbg_assert_(mr[regs[i]].lane == 0); + _dbg_assert_(mr[regs[i] + regLanes[i] - 1].lane == regLanes[i] - 1); + _dbg_assert_(mr[regs[i]].nReg == mr[regs[i] + regLanes[i] - 1].nReg); + } else { + _dbg_assert_(mr[regs[i]].lane == -1); + } + } + } +} + IRNativeReg IRNativeRegCacheBase::MapNativeReg(MIPSLoc type, IRReg first, int lanes, MIPSMap flags) { _assert_msg_(first != IRREG_INVALID, "Cannot map invalid register"); _assert_msg_(lanes >= 1 && lanes <= 4, "Cannot map %d lanes", lanes); diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index 33779b701a..7bc0460f6e 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -183,6 +183,7 @@ public: void MarkGPRDirty(IRReg gpr, bool andNormalized32 = false); void MarkGPRAsPointerDirty(IRReg gpr); + virtual void Map(const IRInst &inst); virtual void FlushAll(); protected: @@ -224,7 +225,12 @@ protected: MIPSComp::JitOptions *jo_; const MIPSComp::IRBlock *irBlock_ = nullptr; int irIndex_ = 0; - int totalNativeRegs_ = 0; + + struct { + int totalNativeRegs = 0; + bool mapSIMD = false; + bool mapUseVRegs = false; + } config_; RegStatusNative nr[TOTAL_POSSIBLE_NATIVEREGS]; RegStatusMIPS mr[TOTAL_MAPPABLE_IRREGS]; diff --git a/Core/MIPS/RiscV/RiscVCompALU.cpp b/Core/MIPS/RiscV/RiscVCompALU.cpp index 69e4a47ee5..fbaf45c0af 100644 --- a/Core/MIPS/RiscV/RiscVCompALU.cpp +++ b/Core/MIPS/RiscV/RiscVCompALU.cpp @@ -53,13 +53,15 @@ void RiscVJitBackend::CompIR_Arith(IRInst inst) { switch (inst.op) { case IROp::Add: - gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); ADDW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + gpr.MarkGPRDirty(inst.dest, true); break; case IROp::Sub: - gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); SUBW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + gpr.MarkGPRDirty(inst.dest, true); break; case IROp::AddConst: @@ -69,25 +71,29 @@ void RiscVJitBackend::CompIR_Arith(IRInst inst) { gpr.MarkGPRAsPointerDirty(inst.dest); ADDI(gpr.RPtr(inst.dest), gpr.RPtr(inst.dest), inst.constant); } else { - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); ADDIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant); + gpr.MarkGPRDirty(inst.dest, true); } } else { - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); LI(SCRATCH1, (int32_t)inst.constant); ADDW(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); + gpr.MarkGPRDirty(inst.dest, true); } break; case IROp::SubConst: - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); LI(SCRATCH1, (int32_t)inst.constant); SUBW(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); + gpr.MarkGPRDirty(inst.dest, true); break; case IROp::Neg: - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); SUBW(gpr.R(inst.dest), R_ZERO, gpr.R(inst.src1)); + gpr.MarkGPRDirty(inst.dest, true); break; default: @@ -99,13 +105,14 @@ void RiscVJitBackend::CompIR_Arith(IRInst inst) { void RiscVJitBackend::CompIR_Logic(IRInst inst) { CONDITIONAL_DISABLE; + bool resultNormalized = false; switch (inst.op) { case IROp::And: if (inst.src1 != inst.src2) { - gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + gpr.Map(inst); AND(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); } else if (inst.src1 != inst.dest) { - gpr.MapDirtyIn(inst.dest, inst.src1); + gpr.Map(inst); MV(gpr.R(inst.dest), gpr.R(inst.src1)); gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } @@ -113,13 +120,13 @@ void RiscVJitBackend::CompIR_Logic(IRInst inst) { case IROp::Or: if (inst.src1 != inst.src2) { - gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); - OR(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); // If both were normalized before, the result is normalized. - if (gpr.IsNormalized32(inst.src1) && gpr.IsNormalized32(inst.src2)) - gpr.MarkGPRDirty(inst.dest, true); + resultNormalized = gpr.IsNormalized32(inst.src1) && gpr.IsNormalized32(inst.src2); + gpr.Map(inst); + OR(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + gpr.MarkGPRDirty(inst.dest, resultNormalized); } else if (inst.src1 != inst.dest) { - gpr.MapDirtyIn(inst.dest, inst.src1); + gpr.Map(inst); MV(gpr.R(inst.dest), gpr.R(inst.src1)); gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } @@ -129,22 +136,22 @@ void RiscVJitBackend::CompIR_Logic(IRInst inst) { if (inst.src1 == inst.src2) { gpr.SetGPRImm(inst.dest, 0); } else { - gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + gpr.Map(inst); XOR(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); } break; case IROp::AndConst: + resultNormalized = gpr.IsNormalized32(inst.src1); + gpr.Map(inst); if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { - gpr.MapDirtyIn(inst.dest, inst.src1); ANDI(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant); } else { - gpr.MapDirtyIn(inst.dest, inst.src1); LI(SCRATCH1, (int32_t)inst.constant); AND(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); } // If the sign bits aren't cleared, and it was normalized before - it still is. - if ((inst.constant & 0x80000000) != 0 && gpr.IsNormalized32(inst.src1)) + if ((inst.constant & 0x80000000) != 0 && resultNormalized) gpr.MarkGPRDirty(inst.dest, true); // Otherwise, if we cleared the sign bits, it's naturally normalized. else if ((inst.constant & 0x80000000) == 0) @@ -152,32 +159,30 @@ void RiscVJitBackend::CompIR_Logic(IRInst inst) { break; case IROp::OrConst: + resultNormalized = gpr.IsNormalized32(inst.src1); + gpr.Map(inst); if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { - gpr.MapDirtyIn(inst.dest, inst.src1); ORI(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant); } else { - gpr.MapDirtyIn(inst.dest, inst.src1); LI(SCRATCH1, (int32_t)inst.constant); OR(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); } // Since our constant is normalized, oring its bits in won't hurt normalization. - if (gpr.IsNormalized32(inst.src1)) - gpr.MarkGPRDirty(inst.dest, true); + gpr.MarkGPRDirty(inst.dest, resultNormalized); break; case IROp::XorConst: + gpr.Map(inst); if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { - gpr.MapDirtyIn(inst.dest, inst.src1); XORI(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant); } else { - gpr.MapDirtyIn(inst.dest, inst.src1); LI(SCRATCH1, (int32_t)inst.constant); XOR(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); } break; case IROp::Not: - gpr.MapDirtyIn(inst.dest, inst.src1); + gpr.Map(inst); NOT(gpr.R(inst.dest), gpr.R(inst.src1)); break; @@ -193,32 +198,32 @@ void RiscVJitBackend::CompIR_Assign(IRInst inst) { switch (inst.op) { case IROp::Mov: if (inst.dest != inst.src1) { - gpr.MapDirtyIn(inst.dest, inst.src1); + gpr.Map(inst); MV(gpr.R(inst.dest), gpr.R(inst.src1)); gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } break; case IROp::Ext8to32: + gpr.Map(inst); if (cpu_info.RiscV_Zbb) { - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); SEXT_B(gpr.R(inst.dest), gpr.R(inst.src1)); } else { - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); SLLI(gpr.R(inst.dest), gpr.R(inst.src1), 24); SRAIW(gpr.R(inst.dest), gpr.R(inst.dest), 24); } + gpr.MarkGPRDirty(inst.dest, true); break; case IROp::Ext16to32: + gpr.Map(inst); if (cpu_info.RiscV_Zbb) { - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); SEXT_H(gpr.R(inst.dest), gpr.R(inst.src1)); } else { - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); SLLI(gpr.R(inst.dest), gpr.R(inst.src1), 16); SRAIW(gpr.R(inst.dest), gpr.R(inst.dest), 16); } + gpr.MarkGPRDirty(inst.dest, true); break; default: @@ -233,7 +238,7 @@ void RiscVJitBackend::CompIR_Bits(IRInst inst) { switch (inst.op) { case IROp::ReverseBits: if (cpu_info.RiscV_Zbb) { - gpr.MapDirtyIn(inst.dest, inst.src1); + gpr.Map(inst); // Start by reversing bytes (note: this puts in upper 32 of XLEN.) REV8(gpr.R(inst.dest), gpr.R(inst.src1)); @@ -275,7 +280,7 @@ void RiscVJitBackend::CompIR_Bits(IRInst inst) { case IROp::BSwap32: if (cpu_info.RiscV_Zbb) { - gpr.MapDirtyIn(inst.dest, inst.src1); + gpr.Map(inst); REV8(gpr.R(inst.dest), gpr.R(inst.src1)); if (XLEN >= 64) { // REV8 swaps the entire register, so get the 32 highest bits. @@ -289,9 +294,10 @@ void RiscVJitBackend::CompIR_Bits(IRInst inst) { case IROp::Clz: if (cpu_info.RiscV_Zbb) { - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); // This even sets to 32 when zero, perfect. CLZW(gpr.R(inst.dest), gpr.R(inst.src1)); + gpr.MarkGPRDirty(inst.dest, true); } else { CompIR_Generic(inst); } @@ -308,24 +314,28 @@ void RiscVJitBackend::CompIR_Shift(IRInst inst) { switch (inst.op) { case IROp::Shl: - gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); SLLW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + gpr.MarkGPRDirty(inst.dest, true); break; case IROp::Shr: - gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); SRLW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + gpr.MarkGPRDirty(inst.dest, true); break; case IROp::Sar: - gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); SRAW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + gpr.MarkGPRDirty(inst.dest, true); break; case IROp::Ror: if (cpu_info.RiscV_Zbb) { - gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); RORW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + gpr.MarkGPRDirty(inst.dest, true); } else { CompIR_Generic(inst); } @@ -337,13 +347,14 @@ void RiscVJitBackend::CompIR_Shift(IRInst inst) { gpr.SetGPRImm(inst.dest, 0); } else if (inst.src2 == 0) { if (inst.dest != inst.src1) { - gpr.MapDirtyIn(inst.dest, inst.src1); + gpr.Map(inst); MV(gpr.R(inst.dest), gpr.R(inst.src1)); gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } } else { - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); SLLIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2); + gpr.MarkGPRDirty(inst.dest, true); } break; @@ -353,43 +364,47 @@ void RiscVJitBackend::CompIR_Shift(IRInst inst) { gpr.SetGPRImm(inst.dest, 0); } else if (inst.src2 == 0) { if (inst.dest != inst.src1) { - gpr.MapDirtyIn(inst.dest, inst.src1); + gpr.Map(inst); MV(gpr.R(inst.dest), gpr.R(inst.src1)); gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } } else { - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); SRLIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2); + gpr.MarkGPRDirty(inst.dest, true); } break; case IROp::SarImm: // Shouldn't happen, but let's be safe of any passes that modify the ops. if (inst.src2 >= 32) { - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); SRAIW(gpr.R(inst.dest), gpr.R(inst.src1), 31); + gpr.MarkGPRDirty(inst.dest, true); } else if (inst.src2 == 0) { if (inst.dest != inst.src1) { - gpr.MapDirtyIn(inst.dest, inst.src1); + gpr.Map(inst); MV(gpr.R(inst.dest), gpr.R(inst.src1)); gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } } else { - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); SRAIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2); + gpr.MarkGPRDirty(inst.dest, true); } break; case IROp::RorImm: if (inst.src2 == 0) { if (inst.dest != inst.src1) { - gpr.MapDirtyIn(inst.dest, inst.src1); + gpr.Map(inst); MV(gpr.R(inst.dest), gpr.R(inst.src1)); gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } } else if (cpu_info.RiscV_Zbb) { - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); RORIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2 & 31); + gpr.MarkGPRDirty(inst.dest, true); } else { CompIR_Generic(inst); } @@ -408,25 +423,22 @@ void RiscVJitBackend::CompIR_Compare(IRInst inst) { RiscVReg rhs = INVALID_REG; switch (inst.op) { case IROp::Slt: - gpr.SpillLockGPR(inst.dest, inst.src1, inst.src2); - gpr.MapReg(inst.src1); - gpr.MapReg(inst.src2); + gpr.Map(inst); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); - gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); SLT(gpr.R(inst.dest), lhs, rhs); + gpr.MarkGPRDirty(inst.dest, true); break; case IROp::SltConst: if (inst.constant == 0) { // Basically, getting the sign bit. Let's shift instead. - gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + gpr.Map(inst); SRLIW(gpr.R(inst.dest), gpr.R(inst.src1), 31); + gpr.MarkGPRDirty(inst.dest, true); } else { - gpr.SpillLockGPR(inst.dest, inst.src1); - gpr.MapReg(inst.src1); + gpr.Map(inst); NormalizeSrc1(inst, &lhs, SCRATCH1, false); - gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { SLTI(gpr.R(inst.dest), lhs, (int32_t)inst.constant); @@ -439,24 +451,20 @@ void RiscVJitBackend::CompIR_Compare(IRInst inst) { break; case IROp::SltU: - gpr.SpillLockGPR(inst.dest, inst.src1, inst.src2); - gpr.MapReg(inst.src1); - gpr.MapReg(inst.src2); + gpr.Map(inst); // It's still fine to sign extend, the biggest just get even bigger. NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); - gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); SLTU(gpr.R(inst.dest), lhs, rhs); + gpr.MarkGPRDirty(inst.dest, true); break; case IROp::SltUConst: if (inst.constant == 0) { gpr.SetGPRImm(inst.dest, 0); } else { - gpr.SpillLockGPR(inst.dest, inst.src1); - gpr.MapReg(inst.src1); + gpr.Map(inst); NormalizeSrc1(inst, &lhs, SCRATCH1, false); - gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); // We sign extend because we're comparing against something normalized. // It's also the most efficient to set. @@ -466,6 +474,7 @@ void RiscVJitBackend::CompIR_Compare(IRInst inst) { LI(SCRATCH2, (int32_t)inst.constant); SLTU(gpr.R(inst.dest), lhs, SCRATCH2); } + gpr.MarkGPRDirty(inst.dest, true); } break; @@ -487,8 +496,8 @@ void RiscVJitBackend::CompIR_CondAssign(IRInst inst) { if (inst.dest == inst.src2) return; - // We could have a "zero" that with wrong upper due to XOR, so we have to normalize. - gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::ALWAYS_LOAD); + // We could have a "zero" with wrong upper due to XOR, so we have to normalize. + gpr.Map(inst); NormalizeSrc1(inst, &lhs, SCRATCH1, true); switch (inst.op) { @@ -510,7 +519,7 @@ void RiscVJitBackend::CompIR_CondAssign(IRInst inst) { case IROp::Max: if (inst.src1 != inst.src2) { if (cpu_info.RiscV_Zbb) { - gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + gpr.Map(inst); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); MAX(gpr.R(inst.dest), lhs, rhs); // Because we had to normalize the inputs, the output is normalized. @@ -519,7 +528,7 @@ void RiscVJitBackend::CompIR_CondAssign(IRInst inst) { CompIR_Generic(inst); } } else if (inst.dest != inst.src1) { - gpr.MapDirtyIn(inst.dest, inst.src1); + gpr.Map(inst); MV(gpr.R(inst.dest), gpr.R(inst.src1)); gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } @@ -528,7 +537,7 @@ void RiscVJitBackend::CompIR_CondAssign(IRInst inst) { case IROp::Min: if (inst.src1 != inst.src2) { if (cpu_info.RiscV_Zbb) { - gpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + gpr.Map(inst); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); MIN(gpr.R(inst.dest), lhs, rhs); // Because we had to normalize the inputs, the output is normalized. @@ -537,7 +546,7 @@ void RiscVJitBackend::CompIR_CondAssign(IRInst inst) { CompIR_Generic(inst); } } else if (inst.dest != inst.src1) { - gpr.MapDirtyIn(inst.dest, inst.src1); + gpr.Map(inst); MV(gpr.R(inst.dest), gpr.R(inst.src1)); gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); } diff --git a/Core/MIPS/RiscV/RiscVCompBranch.cpp b/Core/MIPS/RiscV/RiscVCompBranch.cpp index 4b0f141649..f146fdd1ee 100644 --- a/Core/MIPS/RiscV/RiscVCompBranch.cpp +++ b/Core/MIPS/RiscV/RiscVCompBranch.cpp @@ -45,7 +45,7 @@ void RiscVJitBackend::CompIR_Exit(IRInst inst) { break; case IROp::ExitToReg: - exitReg = gpr.MapReg(inst.src1); + exitReg = gpr.MapGPR(inst.src1); FlushAll(); // TODO: If ever we don't read this back in dispatcherPCInSCRATCH1_, we should zero upper. MV(SCRATCH1, exitReg); @@ -72,7 +72,7 @@ void RiscVJitBackend::CompIR_ExitIf(IRInst inst) { switch (inst.op) { case IROp::ExitToConstIfEq: case IROp::ExitToConstIfNeq: - gpr.MapInIn(inst.src1, inst.src2); + gpr.Map(inst); // We can't use SCRATCH1, which is destroyed by FlushAll()... but cheat and use R_RA. NormalizeSrc12(inst, &lhs, &rhs, R_RA, SCRATCH2, true); FlushAll(); @@ -99,7 +99,7 @@ void RiscVJitBackend::CompIR_ExitIf(IRInst inst) { case IROp::ExitToConstIfGeZ: case IROp::ExitToConstIfLtZ: case IROp::ExitToConstIfLeZ: - gpr.MapReg(inst.src1); + gpr.Map(inst); NormalizeSrc1(inst, &lhs, SCRATCH2, true); FlushAll(); diff --git a/Core/MIPS/RiscV/RiscVCompFPU.cpp b/Core/MIPS/RiscV/RiscVCompFPU.cpp index 7b92468713..5e668ce907 100644 --- a/Core/MIPS/RiscV/RiscVCompFPU.cpp +++ b/Core/MIPS/RiscV/RiscVCompFPU.cpp @@ -39,34 +39,34 @@ void RiscVJitBackend::CompIR_FArith(IRInst inst) { switch (inst.op) { case IROp::FAdd: - fpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + fpr.Map(inst); FADD(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); break; case IROp::FSub: - fpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + fpr.Map(inst); FSUB(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); break; case IROp::FMul: - fpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + fpr.Map(inst); // We'll assume everyone will make it such that 0 * infinity = NAN properly. // See blame on this comment if that proves untrue. FMUL(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); break; case IROp::FDiv: - fpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + fpr.Map(inst); FDIV(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); break; case IROp::FSqrt: - fpr.MapDirtyIn(inst.dest, inst.src1); + fpr.Map(inst); FSQRT(32, fpr.R(inst.dest), fpr.R(inst.src1)); break; case IROp::FNeg: - fpr.MapDirtyIn(inst.dest, inst.src1); + fpr.Map(inst); FNEG(32, fpr.R(inst.dest), fpr.R(inst.src1)); break; @@ -83,7 +83,7 @@ void RiscVJitBackend::CompIR_FCondAssign(IRInst inst) { bool maxCondition = inst.op == IROp::FMax; // FMin and FMax are used by VFPU and handle NAN/INF as just a larger exponent. - fpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2); + fpr.Map(inst); FCLASS(32, SCRATCH1, fpr.R(inst.src1)); FCLASS(32, SCRATCH2, fpr.R(inst.src2)); @@ -146,19 +146,19 @@ void RiscVJitBackend::CompIR_FAssign(IRInst inst) { switch (inst.op) { case IROp::FMov: if (inst.dest != inst.src1) { - fpr.MapDirtyIn(inst.dest, inst.src1); + fpr.Map(inst); FMV(32, fpr.R(inst.dest), fpr.R(inst.src1)); } break; case IROp::FAbs: - fpr.MapDirtyIn(inst.dest, inst.src1); + fpr.Map(inst); FABS(32, fpr.R(inst.dest), fpr.R(inst.src1)); break; case IROp::FSign: { - fpr.MapDirtyIn(inst.dest, inst.src1); + fpr.Map(inst); // Check if it's negative zero, either 0x10/0x08 is zero. FCLASS(32, SCRATCH1, fpr.R(inst.src1)); ANDI(SCRATCH1, SCRATCH1, 0x18); @@ -190,7 +190,7 @@ void RiscVJitBackend::CompIR_FRound(IRInst inst) { CONDITIONAL_DISABLE; // TODO: If this is followed by a GPR transfer, might want to combine. - fpr.MapDirtyIn(inst.dest, inst.src1); + fpr.Map(inst); switch (inst.op) { case IROp::FRound: @@ -228,7 +228,7 @@ void RiscVJitBackend::CompIR_FCvt(IRInst inst) { case IROp::FCvtSW: // TODO: This is probably proceeded by a GPR transfer, might be ideal to combine. - fpr.MapDirtyIn(inst.dest, inst.src1); + fpr.Map(inst); FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1)); FCVT(FConv::S, FConv::W, fpr.R(inst.dest), SCRATCH1); break; @@ -340,7 +340,7 @@ void RiscVJitBackend::CompIR_FSat(IRInst inst) { void RiscVJitBackend::CompIR_FCompare(IRInst inst) { CONDITIONAL_DISABLE; - constexpr IRReg IRREG_VFPUL_CC = IRREG_VFPU_CTRL_BASE + VFPU_CTRL_CC; + constexpr IRReg IRREG_VFPU_CC = IRREG_VFPU_CTRL_BASE + VFPU_CTRL_CC; switch (inst.op) { case IROp::FCmp: @@ -350,8 +350,8 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case IRFpCompareMode::EitherUnordered: - fpr.MapInIn(inst.src1, inst.src2); - gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + fpr.Map(inst); + gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); FCLASS(32, SCRATCH1, fpr.R(inst.src1)); FCLASS(32, SCRATCH2, fpr.R(inst.src2)); OR(SCRATCH1, SCRATCH1, SCRATCH2); @@ -361,14 +361,14 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case IRFpCompareMode::EqualOrdered: - fpr.MapInIn(inst.src1, inst.src2); - gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + fpr.Map(inst); + gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); FEQ(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2)); break; case IRFpCompareMode::EqualUnordered: - fpr.MapInIn(inst.src1, inst.src2); - gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + fpr.Map(inst); + gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); FEQ(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2)); // Now let's just OR in the unordered check. @@ -382,27 +382,27 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case IRFpCompareMode::LessEqualOrdered: - fpr.MapInIn(inst.src1, inst.src2); - gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + fpr.Map(inst); + gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); FLE(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2)); break; case IRFpCompareMode::LessEqualUnordered: - fpr.MapInIn(inst.src1, inst.src2); - gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + fpr.Map(inst); + gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); FLT(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src2), fpr.R(inst.src1)); SEQZ(gpr.R(IRREG_FPCOND), gpr.R(IRREG_FPCOND)); break; case IRFpCompareMode::LessOrdered: - fpr.MapInIn(inst.src1, inst.src2); - gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + fpr.Map(inst); + gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); FLT(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2)); break; case IRFpCompareMode::LessUnordered: - fpr.MapInIn(inst.src1, inst.src2); - gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + fpr.Map(inst); + gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); FLE(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src2), fpr.R(inst.src1)); SEQZ(gpr.R(IRREG_FPCOND), gpr.R(IRREG_FPCOND)); break; @@ -410,14 +410,14 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case IROp::FCmovVfpuCC: - gpr.MapReg(IRREG_VFPUL_CC); - fpr.MapDirtyIn(inst.dest, inst.src1, false); + gpr.MapGPR(IRREG_VFPU_CC); + fpr.Map(inst); if ((inst.src2 & 0xF) == 0) { - ANDI(SCRATCH1, gpr.R(IRREG_VFPUL_CC), 1); + ANDI(SCRATCH1, gpr.R(IRREG_VFPU_CC), 1); } else if (cpu_info.RiscV_Zbs) { - BEXTI(SCRATCH1, gpr.R(IRREG_VFPUL_CC), inst.src2 & 0xF); + BEXTI(SCRATCH1, gpr.R(IRREG_VFPU_CC), inst.src2 & 0xF); } else { - SRLI(SCRATCH1, gpr.R(IRREG_VFPUL_CC), inst.src2 & 0xF); + SRLI(SCRATCH1, gpr.R(IRREG_VFPU_CC), inst.src2 & 0xF); ANDI(SCRATCH1, SCRATCH1, 1); } if ((inst.src2 >> 7) & 1) { @@ -432,15 +432,15 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case IROp::FCmpVfpuBit: - gpr.MapReg(IRREG_VFPUL_CC, MIPSMap::DIRTY); + gpr.MapGPR(IRREG_VFPU_CC, MIPSMap::DIRTY); switch (VCondition(inst.dest & 0xF)) { case VC_EQ: - fpr.MapInIn(inst.src1, inst.src2); + fpr.Map(inst); FEQ(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2)); break; case VC_NE: - fpr.MapInIn(inst.src1, inst.src2); + fpr.Map(inst); // We could almost negate FEQ, except NAN != NAN. // Anything != NAN is false and NAN != NAN is within that, so we only check one side. FCLASS(32, SCRATCH2, fpr.R(inst.src2)); @@ -454,24 +454,24 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { OR(SCRATCH1, SCRATCH1, SCRATCH2); break; case VC_LT: - fpr.MapInIn(inst.src1, inst.src2); + fpr.Map(inst); FLT(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2)); break; case VC_LE: - fpr.MapInIn(inst.src1, inst.src2); + fpr.Map(inst); FLE(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2)); break; case VC_GT: - fpr.MapInIn(inst.src1, inst.src2); + fpr.Map(inst); FLT(32, SCRATCH1, fpr.R(inst.src2), fpr.R(inst.src1)); break; case VC_GE: - fpr.MapInIn(inst.src1, inst.src2); + fpr.Map(inst); FLE(32, SCRATCH1, fpr.R(inst.src2), fpr.R(inst.src1)); break; case VC_EZ: case VC_NZ: - fpr.MapReg(inst.src1); + fpr.MapFPR(inst.src1); // Zero is either 0x10 or 0x08. FCLASS(32, SCRATCH1, fpr.R(inst.src1)); ANDI(SCRATCH1, SCRATCH1, 0x18); @@ -482,7 +482,7 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case VC_EN: case VC_NN: - fpr.MapReg(inst.src1); + fpr.MapFPR(inst.src1); // NAN is either 0x100 or 0x200. FCLASS(32, SCRATCH1, fpr.R(inst.src1)); ANDI(SCRATCH1, SCRATCH1, 0x300); @@ -493,7 +493,7 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case VC_EI: case VC_NI: - fpr.MapReg(inst.src1); + fpr.MapFPR(inst.src1); // Infinity is either 0x80 or 0x01. FCLASS(32, SCRATCH1, fpr.R(inst.src1)); ANDI(SCRATCH1, SCRATCH1, 0x81); @@ -504,7 +504,7 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case VC_ES: case VC_NS: - fpr.MapReg(inst.src1); + fpr.MapFPR(inst.src1); // Infinity is either 0x80 or 0x01, NAN is either 0x100 or 0x200. FCLASS(32, SCRATCH1, fpr.R(inst.src1)); ANDI(SCRATCH1, SCRATCH1, 0x381); @@ -521,15 +521,15 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; } - ANDI(gpr.R(IRREG_VFPUL_CC), gpr.R(IRREG_VFPUL_CC), ~(1 << (inst.dest >> 4))); + ANDI(gpr.R(IRREG_VFPU_CC), gpr.R(IRREG_VFPU_CC), ~(1 << (inst.dest >> 4))); if ((inst.dest >> 4) != 0) SLLI(SCRATCH1, SCRATCH1, inst.dest >> 4); - OR(gpr.R(IRREG_VFPUL_CC), gpr.R(IRREG_VFPUL_CC), SCRATCH1); + OR(gpr.R(IRREG_VFPU_CC), gpr.R(IRREG_VFPU_CC), SCRATCH1); break; case IROp::FCmpVfpuAggregate: - gpr.MapReg(IRREG_VFPUL_CC, MIPSMap::DIRTY); - ANDI(SCRATCH1, gpr.R(IRREG_VFPUL_CC), inst.dest); + gpr.MapGPR(IRREG_VFPU_CC, MIPSMap::DIRTY); + ANDI(SCRATCH1, gpr.R(IRREG_VFPU_CC), inst.dest); // This is the "any bit", easy. SNEZ(SCRATCH2, SCRATCH1); // To compare to inst.dest for "all", let's simply subtract it and compare to zero. @@ -541,8 +541,8 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { OR(SCRATCH1, SCRATCH1, SCRATCH2); // Reject those any/all bits and replace them with our own. - ANDI(gpr.R(IRREG_VFPUL_CC), gpr.R(IRREG_VFPUL_CC), ~0x30); - OR(gpr.R(IRREG_VFPUL_CC), gpr.R(IRREG_VFPUL_CC), SCRATCH1); + ANDI(gpr.R(IRREG_VFPU_CC), gpr.R(IRREG_VFPU_CC), ~0x30); + OR(gpr.R(IRREG_VFPU_CC), gpr.R(IRREG_VFPU_CC), SCRATCH1); break; default: @@ -592,7 +592,7 @@ void RiscVJitBackend::CompIR_FSpecial(IRInst inst) { } QuickCallFunction(func, SCRATCH1); - fpr.MapReg(inst.dest, MIPSMap::NOINIT); + fpr.MapFPR(inst.dest, MIPSMap::NOINIT); // If it's already F10, we're done - MapReg doesn't actually overwrite the reg in that case. if (fpr.R(inst.dest) != F10) { FMV(32, fpr.R(inst.dest), F10); @@ -622,7 +622,7 @@ void RiscVJitBackend::CompIR_FSpecial(IRInst inst) { case IROp::FRecip: if (inst.dest != inst.src1) { // This is the easy case. - fpr.MapDirtyIn(inst.dest, inst.src1); + fpr.Map(inst); LI(SCRATCH1, 1.0f); FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); FDIV(32, fpr.R(inst.dest), fpr.R(inst.dest), fpr.R(inst.src1)); diff --git a/Core/MIPS/RiscV/RiscVCompLoadStore.cpp b/Core/MIPS/RiscV/RiscVCompLoadStore.cpp index 348221ef4b..223cb9abe1 100644 --- a/Core/MIPS/RiscV/RiscVCompLoadStore.cpp +++ b/Core/MIPS/RiscV/RiscVCompLoadStore.cpp @@ -35,7 +35,7 @@ using namespace RiscVGen; using namespace RiscVJitConstants; void RiscVJitBackend::SetScratch1ToSrc1Address(IRReg src1) { - gpr.MapReg(src1); + gpr.MapGPR(src1); #ifdef MASKED_PSP_MEMORY SLLIW(SCRATCH1, gpr.R(src1), 2); SRLIW(SCRATCH1, SCRATCH1, 2); @@ -75,13 +75,13 @@ void RiscVJitBackend::CompIR_Load(IRInst inst) { inst.constant &= Memory::MEMVIEW32_MASK; #endif } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { - addrReg = gpr.MapRegAsPointer(inst.src1); + addrReg = gpr.MapGPRAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); addrReg = SCRATCH1; } // With NOINIT, MapReg won't subtract MEMBASEREG even if dest == src1. - gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + gpr.MapGPR(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); @@ -147,7 +147,7 @@ void RiscVJitBackend::CompIR_FLoad(IRInst inst) { inst.constant &= Memory::MEMVIEW32_MASK; #endif } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { - addrReg = gpr.MapRegAsPointer(inst.src1); + addrReg = gpr.MapGPRAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); addrReg = SCRATCH1; @@ -159,7 +159,7 @@ void RiscVJitBackend::CompIR_FLoad(IRInst inst) { switch (inst.op) { case IROp::LoadFloat: - fpr.MapReg(inst.dest, MIPSMap::NOINIT); + fpr.MapFPR(inst.dest, MIPSMap::NOINIT); FL(32, fpr.R(inst.dest), addrReg, imm); break; @@ -180,7 +180,7 @@ void RiscVJitBackend::CompIR_VecLoad(IRInst inst) { inst.constant &= Memory::MEMVIEW32_MASK; #endif } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { - addrReg = gpr.MapRegAsPointer(inst.src1); + addrReg = gpr.MapGPRAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); addrReg = SCRATCH1; @@ -195,7 +195,7 @@ void RiscVJitBackend::CompIR_VecLoad(IRInst inst) { case IROp::LoadVec4: for (int i = 0; i < 4; ++i) { // Spilling is okay. - fpr.MapReg(inst.dest + i, MIPSMap::NOINIT); + fpr.MapFPR(inst.dest + i, MIPSMap::NOINIT); FL(32, fpr.R(inst.dest + i), addrReg, imm + 4 * i); } break; @@ -218,14 +218,14 @@ void RiscVJitBackend::CompIR_Store(IRInst inst) { inst.constant &= Memory::MEMVIEW32_MASK; #endif } else if ((jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) { - addrReg = gpr.MapRegAsPointer(inst.src1); + addrReg = gpr.MapGPRAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); addrReg = SCRATCH1; } RiscVReg valueReg = gpr.TryMapTempImm(inst.src3); if (valueReg == INVALID_REG) - valueReg = gpr.MapReg(inst.src3); + valueReg = gpr.MapGPR(inst.src3); s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); @@ -264,13 +264,13 @@ void RiscVJitBackend::CompIR_CondStore(IRInst inst) { inst.constant &= Memory::MEMVIEW32_MASK; #endif } else if ((jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) { - addrReg = gpr.MapRegAsPointer(inst.src1); + addrReg = gpr.MapGPRAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); addrReg = SCRATCH1; } - gpr.MapReg(inst.src3, inst.dest == MIPS_REG_ZERO ? MIPSMap::INIT : MIPSMap::DIRTY); - gpr.MapReg(IRREG_LLBIT); + gpr.MapGPR(inst.src3, inst.dest == MIPS_REG_ZERO ? MIPSMap::INIT : MIPSMap::DIRTY); + gpr.MapGPR(IRREG_LLBIT); s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); @@ -318,7 +318,7 @@ void RiscVJitBackend::CompIR_FStore(IRInst inst) { inst.constant &= Memory::MEMVIEW32_MASK; #endif } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { - addrReg = gpr.MapRegAsPointer(inst.src1); + addrReg = gpr.MapGPRAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); addrReg = SCRATCH1; @@ -330,7 +330,7 @@ void RiscVJitBackend::CompIR_FStore(IRInst inst) { switch (inst.op) { case IROp::StoreFloat: - fpr.MapReg(inst.src3); + fpr.MapFPR(inst.src3); FS(32, fpr.R(inst.src3), addrReg, imm); break; @@ -351,7 +351,7 @@ void RiscVJitBackend::CompIR_VecStore(IRInst inst) { inst.constant &= Memory::MEMVIEW32_MASK; #endif } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { - addrReg = gpr.MapRegAsPointer(inst.src1); + addrReg = gpr.MapGPRAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); addrReg = SCRATCH1; @@ -366,7 +366,7 @@ void RiscVJitBackend::CompIR_VecStore(IRInst inst) { case IROp::StoreVec4: for (int i = 0; i < 4; ++i) { // Spilling is okay, though not ideal. - fpr.MapReg(inst.src3 + i); + fpr.MapFPR(inst.src3 + i); FS(32, fpr.R(inst.src3 + i), addrReg, imm + 4 * i); } break; diff --git a/Core/MIPS/RiscV/RiscVCompSystem.cpp b/Core/MIPS/RiscV/RiscVCompSystem.cpp index ce41e6faf0..b655bbda74 100644 --- a/Core/MIPS/RiscV/RiscVCompSystem.cpp +++ b/Core/MIPS/RiscV/RiscVCompSystem.cpp @@ -49,7 +49,7 @@ void RiscVJitBackend::CompIR_Basic(IRInst inst) { break; case IROp::SetConstF: - fpr.MapReg(inst.dest, MIPSMap::NOINIT); + fpr.Map(inst); if (inst.constant == 0) { FCVT(FConv::S, FConv::W, fpr.R(inst.dest), R_ZERO); } else { @@ -70,7 +70,7 @@ void RiscVJitBackend::CompIR_Basic(IRInst inst) { break; case IROp::SetPC: - gpr.MapIn(inst.src1); + gpr.Map(inst); MovToPC(gpr.R(inst.src1)); break; @@ -100,8 +100,8 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { break; case IROp::SetCtrlVFPUFReg: - gpr.MapReg(IRREG_VFPU_CTRL_BASE + inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - fpr.MapReg(inst.src1); + gpr.MapGPR(IRREG_VFPU_CTRL_BASE + inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + fpr.MapFPR(inst.src1); FMV(FMv::X, FMv::W, gpr.R(IRREG_VFPU_CTRL_BASE + inst.dest), fpr.R(inst.src1)); break; @@ -157,18 +157,18 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { break; case IROp::FMovFromGPR: - fpr.MapReg(inst.dest, MIPSMap::NOINIT); + fpr.MapFPR(inst.dest, MIPSMap::NOINIT); if (gpr.IsGPRImm(inst.src1) && gpr.GetGPRImm(inst.src1) == 0) { FCVT(FConv::S, FConv::W, fpr.R(inst.dest), R_ZERO); } else { - gpr.MapReg(inst.src1); + gpr.MapGPR(inst.src1); FMV(FMv::W, FMv::X, fpr.R(inst.dest), gpr.R(inst.src1)); } break; case IROp::FMovToGPR: - gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - fpr.MapReg(inst.src1); + gpr.MapGPR(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + fpr.MapFPR(inst.src1); FMV(FMv::X, FMv::W, gpr.R(inst.dest), fpr.R(inst.src1)); break; diff --git a/Core/MIPS/RiscV/RiscVCompVec.cpp b/Core/MIPS/RiscV/RiscVCompVec.cpp index 3e0f5c1d34..c196c3f68b 100644 --- a/Core/MIPS/RiscV/RiscVCompVec.cpp +++ b/Core/MIPS/RiscV/RiscVCompVec.cpp @@ -40,10 +40,7 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) { switch (inst.op) { case IROp::Vec4Init: - for (int i = 0; i < 4; ++i) - fpr.SpillLockFPR(inst.dest + i); - for (int i = 0; i < 4; ++i) - fpr.MapReg(inst.dest + i, MIPSMap::NOINIT); + fpr.Map(inst); // TODO: Check if FCVT/FMV/FL is better. switch ((Vec4Init)inst.src1) { @@ -161,7 +158,7 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) { moveChained({ neededByDepth2, neededBy, i, foundIn }, neededByDepth3 == foundIn); } } else { - fpr.Map4DirtyIn(inst.dest, inst.src1); + fpr.Map(inst); for (int i = 0; i < 4; ++i) { int lane = (inst.src2 >> (i * 2)) & 3; FMV(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + lane)); @@ -170,7 +167,7 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) { break; case IROp::Vec4Blend: - fpr.Map4DirtyInIn(inst.dest, inst.src1, inst.src2); + fpr.Map(inst); for (int i = 0; i < 4; ++i) { int which = (inst.constant >> i) & 1; FMV(32, fpr.R(inst.dest + i), fpr.R((which ? inst.src2 : inst.src1) + i)); @@ -178,7 +175,7 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) { break; case IROp::Vec4Mov: - fpr.Map4DirtyIn(inst.dest, inst.src1); + fpr.Map(inst); for (int i = 0; i < 4; ++i) FMV(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i)); break; @@ -194,45 +191,45 @@ void RiscVJitBackend::CompIR_VecArith(IRInst inst) { switch (inst.op) { case IROp::Vec4Add: - fpr.Map4DirtyInIn(inst.dest, inst.src1, inst.src2); + fpr.Map(inst); for (int i = 0; i < 4; ++i) FADD(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i)); break; case IROp::Vec4Sub: - fpr.Map4DirtyInIn(inst.dest, inst.src1, inst.src2); + fpr.Map(inst); for (int i = 0; i < 4; ++i) FSUB(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i)); break; case IROp::Vec4Mul: - fpr.Map4DirtyInIn(inst.dest, inst.src1, inst.src2); + fpr.Map(inst); for (int i = 0; i < 4; ++i) FMUL(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i)); break; case IROp::Vec4Div: - fpr.Map4DirtyInIn(inst.dest, inst.src1, inst.src2); + fpr.Map(inst); for (int i = 0; i < 4; ++i) FDIV(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i)); break; case IROp::Vec4Scale: fpr.SpillLockFPR(inst.src2); - fpr.MapReg(inst.src2); + fpr.MapFPR(inst.src2); fpr.Map4DirtyIn(inst.dest, inst.src1); for (int i = 0; i < 4; ++i) FMUL(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2)); break; case IROp::Vec4Neg: - fpr.Map4DirtyIn(inst.dest, inst.src1); + fpr.Map(inst); for (int i = 0; i < 4; ++i) FNEG(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i)); break; case IROp::Vec4Abs: - fpr.Map4DirtyIn(inst.dest, inst.src1); + fpr.Map(inst); for (int i = 0; i < 4; ++i) FABS(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i)); break; @@ -255,10 +252,10 @@ void RiscVJitBackend::CompIR_VecHoriz(IRInst inst) { fpr.SpillLockFPR(inst.src2 + i); } for (int i = 0; i < 4; ++i) { - fpr.MapReg(inst.src1 + i); - fpr.MapReg(inst.src2 + i); + fpr.MapFPR(inst.src1 + i); + fpr.MapFPR(inst.src2 + i); } - fpr.MapReg(inst.dest, MIPSMap::NOINIT); + fpr.MapFPR(inst.dest, MIPSMap::NOINIT); if ((inst.dest < inst.src1 + 4 && inst.dest >= inst.src1) || (inst.dest < inst.src2 + 4 && inst.dest >= inst.src2)) { // This means inst.dest overlaps one of src1 or src2. We have to do that one first. @@ -298,9 +295,9 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { fpr.SpillLockFPR(inst.src1); for (int i = 0; i < 4; ++i) fpr.SpillLockFPR(inst.dest + i); - fpr.MapReg(inst.src1); + fpr.MapFPR(inst.src1); for (int i = 0; i < 4; ++i) - fpr.MapReg(inst.dest + i, MIPSMap::NOINIT); + fpr.MapFPR(inst.dest + i, MIPSMap::NOINIT); FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1)); for (int i = 0; i < 4; ++i) { @@ -319,9 +316,9 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { fpr.SpillLockFPR(inst.src1); for (int i = 0; i < 2; ++i) fpr.SpillLockFPR(inst.dest + i); - fpr.MapReg(inst.src1); + fpr.MapFPR(inst.src1); for (int i = 0; i < 2; ++i) - fpr.MapReg(inst.dest + i, MIPSMap::NOINIT); + fpr.MapFPR(inst.dest + i, MIPSMap::NOINIT); FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1)); SLLI(SCRATCH1, SCRATCH2, 16); @@ -332,7 +329,7 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { break; case IROp::Vec4DuplicateUpperBitsAndShift1: - fpr.Map4DirtyIn(inst.dest, inst.src1); + fpr.Map(inst); for (int i = 0; i < 4; i++) { FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1 + i)); SRLIW(SCRATCH2, SCRATCH1, 8); @@ -348,9 +345,9 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { fpr.SpillLockFPR(inst.dest); for (int i = 0; i < 4; ++i) { fpr.SpillLockFPR(inst.src1 + i); - fpr.MapReg(inst.src1 + i); + fpr.MapFPR(inst.src1 + i); } - fpr.MapReg(inst.dest, MIPSMap::NOINIT); + fpr.MapFPR(inst.dest, MIPSMap::NOINIT); for (int i = 0; i < 4; ++i) { FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1 + i)); @@ -394,7 +391,7 @@ void RiscVJitBackend::CompIR_VecClamp(IRInst inst) { switch (inst.op) { case IROp::Vec4ClampToZero: - fpr.Map4DirtyIn(inst.dest, inst.src1); + fpr.Map(inst); for (int i = 0; i < 4; i++) { FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1 + i)); SRAIW(SCRATCH2, SCRATCH1, 31); diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index e364a2ab97..976c3abaaf 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -32,7 +32,7 @@ using namespace RiscVJitConstants; RiscVRegCache::RiscVRegCache(MIPSComp::JitOptions *jo) : IRNativeRegCacheBase(jo) { // TODO: Move to using for FPRs and VPRs too? - totalNativeRegs_ = NUM_RVREG; + config_.totalNativeRegs = NUM_RVREG; } void RiscVRegCache::Init(RiscVEmitter *emitter) { @@ -220,7 +220,7 @@ RiscVReg RiscVRegCache::GetAndLockTempR() { return reg; } -RiscVReg RiscVRegCache::MapReg(IRReg mipsReg, MIPSMap mapFlags) { +RiscVReg RiscVRegCache::MapGPR(IRReg mipsReg, MIPSMap mapFlags) { _dbg_assert_(IsValidGPR(mipsReg)); // Okay, not mapped, so we need to allocate an RV register. @@ -228,60 +228,28 @@ RiscVReg RiscVRegCache::MapReg(IRReg mipsReg, MIPSMap mapFlags) { return (RiscVReg)nreg; } -RiscVReg RiscVRegCache::MapRegAsPointer(IRReg reg) { +RiscVReg RiscVRegCache::MapGPRAsPointer(IRReg reg) { return (RiscVReg)MapNativeRegAsPointer(reg); } -void RiscVRegCache::MapIn(IRReg rs) { - MapReg(rs); -} - -void RiscVRegCache::MapInIn(IRReg rd, IRReg rs) { - SpillLockGPR(rd, rs); - MapReg(rd); - MapReg(rs); - ReleaseSpillLockGPR(rd, rs); -} - void RiscVRegCache::MapDirtyIn(IRReg rd, IRReg rs, MapType type) { SpillLockGPR(rd, rs); bool load = type == MapType::ALWAYS_LOAD || rd == rs; MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; - MapReg(rd, (load ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); - MapReg(rs); + MapGPR(rd, (load ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); + MapGPR(rs); ReleaseSpillLockGPR(rd, rs); } -void RiscVRegCache::MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, MapType type) { - SpillLockGPR(rd, rs, rt); - bool load = type == MapType::ALWAYS_LOAD || (rd == rs || rd == rt); - MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; - MapReg(rd, (load ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); - MapReg(rt); - MapReg(rs); - ReleaseSpillLockGPR(rd, rs, rt); -} - -void RiscVRegCache::MapDirtyDirtyIn(IRReg rd1, IRReg rd2, IRReg rs, MapType type) { - SpillLockGPR(rd1, rd2, rs); - bool load1 = type == MapType::ALWAYS_LOAD || rd1 == rs; - bool load2 = type == MapType::ALWAYS_LOAD || rd2 == rs; - MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; - MapReg(rd1, (load1 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); - MapReg(rd2, (load2 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); - MapReg(rs); - ReleaseSpillLockGPR(rd1, rd2, rs); -} - void RiscVRegCache::MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, MapType type) { SpillLockGPR(rd1, rd2, rs, rt); bool load1 = type == MapType::ALWAYS_LOAD || (rd1 == rs || rd1 == rt); bool load2 = type == MapType::ALWAYS_LOAD || (rd2 == rs || rd2 == rt); MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; - MapReg(rd1, (load1 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); - MapReg(rd2, (load2 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); - MapReg(rt); - MapReg(rs); + MapGPR(rd1, (load1 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); + MapGPR(rd2, (load2 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); + MapGPR(rt); + MapGPR(rs); ReleaseSpillLockGPR(rd1, rd2, rs, rt); } diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index 9080c4c783..2d0619cca5 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -52,19 +52,15 @@ public: // May fail and return INVALID_REG if it needs flushing. RiscVGen::RiscVReg TryMapTempImm(IRReg); - // Returns an ARM register containing the requested MIPS register. - RiscVGen::RiscVReg MapReg(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); - RiscVGen::RiscVReg MapRegAsPointer(IRReg reg); + // Returns an RV register containing the requested MIPS register. + RiscVGen::RiscVReg MapGPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); + RiscVGen::RiscVReg MapGPRAsPointer(IRReg reg); bool IsNormalized32(IRReg reg); // Copies to another reg if specified, otherwise same reg. RiscVGen::RiscVReg Normalize32(IRReg reg, RiscVGen::RiscVReg destReg = RiscVGen::INVALID_REG); - void MapIn(IRReg rs); - void MapInIn(IRReg rd, IRReg rs); void MapDirtyIn(IRReg rd, IRReg rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); - void MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); - void MapDirtyDirtyIn(IRReg rd1, IRReg rd2, IRReg rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); void MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); void FlushBeforeCall(); void FlushR(IRReg r); diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp index 604cebfdfe..aa9518cde9 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp @@ -31,7 +31,10 @@ using namespace RiscVJitConstants; RiscVRegCacheFPU::RiscVRegCacheFPU(MIPSComp::JitOptions *jo) : IRNativeRegCacheBase(jo) { - totalNativeRegs_ = NUM_RVFPUREG; + config_.totalNativeRegs = NUM_RVFPUREG; + // TODO: When using RISC-V V. + config_.mapUseVRegs = false; + config_.mapSIMD = false; } void RiscVRegCacheFPU::Init(RiscVEmitter *emitter) { @@ -53,7 +56,7 @@ const int *RiscVRegCacheFPU::GetAllocationOrder(MIPSLoc type, int &count, int &b return allocationOrder; } -RiscVReg RiscVRegCacheFPU::MapReg(IRReg mipsReg, MIPSMap mapFlags) { +RiscVReg RiscVRegCacheFPU::MapFPR(IRReg mipsReg, MIPSMap mapFlags) { _dbg_assert_(IsValidFPR(mipsReg)); _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::MEM || mr[mipsReg + 32].loc == MIPSLoc::FREG); @@ -63,35 +66,20 @@ RiscVReg RiscVRegCacheFPU::MapReg(IRReg mipsReg, MIPSMap mapFlags) { return INVALID_REG; } -void RiscVRegCacheFPU::MapInIn(IRReg rd, IRReg rs) { - SpillLockFPR(rd, rs); - MapReg(rd); - MapReg(rs); - ReleaseSpillLockFPR(rd, rs); -} - -void RiscVRegCacheFPU::MapDirtyIn(IRReg rd, IRReg rs, bool avoidLoad) { - SpillLockFPR(rd, rs); - bool load = !avoidLoad || rd == rs; - MapReg(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); - MapReg(rs); - ReleaseSpillLockFPR(rd, rs); -} - void RiscVRegCacheFPU::MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad) { SpillLockFPR(rd, rs, rt); bool load = !avoidLoad || (rd == rs || rd == rt); - MapReg(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); - MapReg(rt); - MapReg(rs); + MapFPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); + MapFPR(rt); + MapFPR(rs); ReleaseSpillLockFPR(rd, rs, rt); } RiscVReg RiscVRegCacheFPU::MapDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad) { SpillLockFPR(rd, rs); bool load = !avoidLoad || rd == rs; - MapReg(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); - MapReg(rs); + MapFPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); + MapFPR(rs); RiscVReg temp = (RiscVReg)(F0 + AllocateReg(MIPSLoc::FREG)); ReleaseSpillLockFPR(rd, rs); return temp; @@ -102,35 +90,21 @@ void RiscVRegCacheFPU::Map4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad) { SpillLockFPR(rdbase + i, rsbase + i); bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase); for (int i = 0; i < 4; ++i) - MapReg(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); + MapFPR(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); for (int i = 0; i < 4; ++i) - MapReg(rsbase + i); + MapFPR(rsbase + i); for (int i = 0; i < 4; ++i) ReleaseSpillLockFPR(rdbase + i, rsbase + i); } -void RiscVRegCacheFPU::Map4DirtyInIn(IRReg rdbase, IRReg rsbase, IRReg rtbase, bool avoidLoad) { - for (int i = 0; i < 4; ++i) - SpillLockFPR(rdbase + i, rsbase + i, rtbase + i); - bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase) || (rdbase < rtbase + 4 && rdbase + 4 > rtbase); - for (int i = 0; i < 4; ++i) - MapReg(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); - for (int i = 0; i < 4; ++i) - MapReg(rsbase + i); - for (int i = 0; i < 4; ++i) - MapReg(rtbase + i); - for (int i = 0; i < 4; ++i) - ReleaseSpillLockFPR(rdbase + i, rsbase + i, rtbase + i); -} - RiscVReg RiscVRegCacheFPU::Map4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad) { for (int i = 0; i < 4; ++i) SpillLockFPR(rdbase + i, rsbase + i); bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase); for (int i = 0; i < 4; ++i) - MapReg(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); + MapFPR(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); for (int i = 0; i < 4; ++i) - MapReg(rsbase + i); + MapFPR(rsbase + i); RiscVReg temp = (RiscVReg)(F0 + AllocateReg(MIPSLoc::FREG)); for (int i = 0; i < 4; ++i) ReleaseSpillLockFPR(rdbase + i, rsbase + i); diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.h b/Core/MIPS/RiscV/RiscVRegCacheFPU.h index 6b612053e3..ccef3e36b0 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.h +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.h @@ -32,14 +32,11 @@ public: void Init(RiscVGen::RiscVEmitter *emitter); // Returns a RISC-V register containing the requested MIPS register. - RiscVGen::RiscVReg MapReg(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); + RiscVGen::RiscVReg MapFPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); - void MapInIn(IRReg rd, IRReg rs); - void MapDirtyIn(IRReg rd, IRReg rs, bool avoidLoad = true); void MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad = true); RiscVGen::RiscVReg MapDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad = true); void Map4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad = true); - void Map4DirtyInIn(IRReg rdbase, IRReg rsbase, IRReg rtbase, bool avoidLoad = true); RiscVGen::RiscVReg Map4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad = true); void FlushBeforeCall(); void FlushR(IRReg r); From e30fb82a64d6a91f987bcc1f68596c35030fd63c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 00:07:21 -0700 Subject: [PATCH 06/36] riscv: Remove some unused reg funcs. --- Core/MIPS/RiscV/RiscVRegCache.cpp | 10 ---------- Core/MIPS/RiscV/RiscVRegCache.h | 2 -- Core/MIPS/RiscV/RiscVRegCacheFPU.cpp | 10 ---------- Core/MIPS/RiscV/RiscVRegCacheFPU.h | 2 -- 4 files changed, 24 deletions(-) diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index 976c3abaaf..e0351b0b4a 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -352,16 +352,6 @@ void RiscVRegCache::StoreRegValue(IRReg mreg, uint32_t imm) { emit_->SW(storeReg, CTXREG, GetMipsRegOffset(mreg)); } -void RiscVRegCache::DiscardR(IRReg mipsReg) { - _dbg_assert_(IsValidGPRNoZero(mipsReg)); - DiscardReg(mipsReg); -} - -void RiscVRegCache::FlushR(IRReg r) { - _dbg_assert_(IsValidGPRNoZero(r)); - FlushReg(r); -} - RiscVReg RiscVRegCache::R(IRReg mipsReg) { _dbg_assert_(IsValidGPR(mipsReg)); _dbg_assert_(mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM); diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index 2d0619cca5..ccf309207c 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -63,8 +63,6 @@ public: void MapDirtyIn(IRReg rd, IRReg rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); void MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); void FlushBeforeCall(); - void FlushR(IRReg r); - void DiscardR(IRReg r); RiscVGen::RiscVReg GetAndLockTempR(); diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp index aa9518cde9..9b544f450c 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp @@ -143,11 +143,6 @@ void RiscVRegCacheFPU::StoreRegValue(IRReg mreg, uint32_t imm) { _assert_msg_(false, "Storing imms to floats is unsupported"); } -void RiscVRegCacheFPU::FlushR(IRReg r) { - _dbg_assert_(IsValidFPR(r)); - FlushReg(r + 32); -} - void RiscVRegCacheFPU::FlushBeforeCall() { // These registers are not preserved by function calls. for (int i = 0; i <= 7; ++i) { @@ -161,11 +156,6 @@ void RiscVRegCacheFPU::FlushBeforeCall() { } } -void RiscVRegCacheFPU::DiscardR(IRReg r) { - _dbg_assert_(IsValidFPR(r)); - DiscardReg(r + 32); -} - RiscVReg RiscVRegCacheFPU::R(IRReg mipsReg) { _dbg_assert_(IsValidFPR(mipsReg)); _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::FREG); diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.h b/Core/MIPS/RiscV/RiscVRegCacheFPU.h index ccef3e36b0..e651436b48 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.h +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.h @@ -39,8 +39,6 @@ public: void Map4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad = true); RiscVGen::RiscVReg Map4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad = true); void FlushBeforeCall(); - void FlushR(IRReg r); - void DiscardR(IRReg r); RiscVGen::RiscVReg R(IRReg preg); // Returns a cached register From f9bf7de7018fc635c4f945be8f59c8e8dd4f7200 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 00:49:27 -0700 Subject: [PATCH 07/36] riscv: Use a single reg cache. --- CMakeLists.txt | 2 - Core/Core.vcxproj | 2 - Core/Core.vcxproj.filters | 6 - Core/MIPS/IR/IRRegCache.cpp | 11 +- Core/MIPS/IR/IRRegCache.h | 2 +- Core/MIPS/RiscV/RiscVAsm.cpp | 4 +- Core/MIPS/RiscV/RiscVCompALU.cpp | 408 ++++++++++++------------- Core/MIPS/RiscV/RiscVCompBranch.cpp | 6 +- Core/MIPS/RiscV/RiscVCompFPU.cpp | 273 +++++++++-------- Core/MIPS/RiscV/RiscVCompLoadStore.cpp | 90 +++--- Core/MIPS/RiscV/RiscVCompSystem.cpp | 76 ++--- Core/MIPS/RiscV/RiscVCompVec.cpp | 158 +++++----- Core/MIPS/RiscV/RiscVJit.cpp | 28 +- Core/MIPS/RiscV/RiscVJit.h | 4 +- Core/MIPS/RiscV/RiscVRegCache.cpp | 195 +++++++++--- Core/MIPS/RiscV/RiscVRegCache.h | 19 +- Core/MIPS/RiscV/RiscVRegCacheFPU.cpp | 175 ----------- Core/MIPS/RiscV/RiscVRegCacheFPU.h | 60 ---- 18 files changed, 699 insertions(+), 820 deletions(-) delete mode 100644 Core/MIPS/RiscV/RiscVRegCacheFPU.cpp delete mode 100644 Core/MIPS/RiscV/RiscVRegCacheFPU.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1778c9b734..5ae082f168 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1633,8 +1633,6 @@ list(APPEND CoreExtra Core/MIPS/RiscV/RiscVJit.h Core/MIPS/RiscV/RiscVRegCache.cpp Core/MIPS/RiscV/RiscVRegCache.h - Core/MIPS/RiscV/RiscVRegCacheFPU.cpp - Core/MIPS/RiscV/RiscVRegCacheFPU.h GPU/Common/VertexDecoderRiscV.cpp ) diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index b179805790..51ad7941a0 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -605,7 +605,6 @@ - @@ -1179,7 +1178,6 @@ - diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 627456ae70..190a475991 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1216,9 +1216,6 @@ MIPS\RiscV - - MIPS\RiscV - MIPS\RiscV @@ -1998,9 +1995,6 @@ MIPS\RiscV - - MIPS\RiscV - MIPS\IR diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index 9f45e1e7b2..4ee289f6ac 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -563,12 +563,17 @@ void IRNativeRegCacheBase::FlushReg(IRReg mreg) { } } -void IRNativeRegCacheBase::FlushAll() { +void IRNativeRegCacheBase::FlushAll(bool gprs, bool fprs) { // Note: make sure not to change the registers when flushing. // Branching code may expect the native reg to retain its value. for (int i = 1; i < TOTAL_MAPPABLE_IRREGS; i++) { IRReg mipsReg = (IRReg)i; + if (!fprs && i >= 32 && IsValidFPR(mipsReg)) + continue; + if (!gprs && IsValidGPR(mipsReg)) + continue; + if (mr[i].isStatic) { IRNativeReg nreg = mr[i].nReg; // Cannot leave any IMMs in registers, not even MIPSLoc::REG_IMM. @@ -599,6 +604,10 @@ void IRNativeRegCacheBase::FlushAll() { int count = 0; const StaticAllocation *allocs = GetStaticAllocations(count); for (int i = 0; i < count; i++) { + if (!fprs && allocs[i].loc != MIPSLoc::FREG && allocs[i].loc != MIPSLoc::VREG) + continue; + if (!gprs && allocs[i].loc != MIPSLoc::REG) + continue; if (allocs[i].pointerified && !nr[allocs[i].nr].pointerified && jo_->enablePointerify) { // Re-pointerify if (mr[allocs[i].mr].loc == MIPSLoc::REG_IMM) diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index 7bc0460f6e..7b8a627773 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -184,7 +184,7 @@ public: void MarkGPRAsPointerDirty(IRReg gpr); virtual void Map(const IRInst &inst); - virtual void FlushAll(); + virtual void FlushAll(bool gprs = true, bool fprs = true); protected: virtual void SetupInitialRegs(); diff --git a/Core/MIPS/RiscV/RiscVAsm.cpp b/Core/MIPS/RiscV/RiscVAsm.cpp index a5205d7b9b..2bbd420f4f 100644 --- a/Core/MIPS/RiscV/RiscVAsm.cpp +++ b/Core/MIPS/RiscV/RiscVAsm.cpp @@ -51,11 +51,11 @@ void RiscVJitBackend::GenerateFixedCode(MIPSState *mipsState) { if (jo.useStaticAlloc) { saveStaticRegisters_ = AlignCode16(); SW(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount)); - gpr.EmitSaveStaticRegisters(); + regs_.EmitSaveStaticRegisters(); RET(); loadStaticRegisters_ = AlignCode16(); - gpr.EmitLoadStaticRegisters(); + regs_.EmitLoadStaticRegisters(); LW(DOWNCOUNTREG, CTXREG, offsetof(MIPSState, downcount)); RET(); diff --git a/Core/MIPS/RiscV/RiscVCompALU.cpp b/Core/MIPS/RiscV/RiscVCompALU.cpp index fbaf45c0af..eb0cc6dc79 100644 --- a/Core/MIPS/RiscV/RiscVCompALU.cpp +++ b/Core/MIPS/RiscV/RiscVCompALU.cpp @@ -53,47 +53,47 @@ void RiscVJitBackend::CompIR_Arith(IRInst inst) { switch (inst.op) { case IROp::Add: - gpr.Map(inst); - ADDW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); - gpr.MarkGPRDirty(inst.dest, true); + regs_.Map(inst); + ADDW(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2)); + regs_.MarkGPRDirty(inst.dest, true); break; case IROp::Sub: - gpr.Map(inst); - SUBW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); - gpr.MarkGPRDirty(inst.dest, true); + regs_.Map(inst); + SUBW(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2)); + regs_.MarkGPRDirty(inst.dest, true); break; case IROp::AddConst: if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { // Typical of stack pointer updates. - if (gpr.IsGPRMappedAsPointer(inst.dest) && inst.dest == inst.src1 && allowPtrMath) { - gpr.MarkGPRAsPointerDirty(inst.dest); - ADDI(gpr.RPtr(inst.dest), gpr.RPtr(inst.dest), inst.constant); + if (regs_.IsGPRMappedAsPointer(inst.dest) && inst.dest == inst.src1 && allowPtrMath) { + regs_.MarkGPRAsPointerDirty(inst.dest); + ADDI(regs_.RPtr(inst.dest), regs_.RPtr(inst.dest), inst.constant); } else { - gpr.Map(inst); - ADDIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant); - gpr.MarkGPRDirty(inst.dest, true); + regs_.Map(inst); + ADDIW(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant); + regs_.MarkGPRDirty(inst.dest, true); } } else { - gpr.Map(inst); + regs_.Map(inst); LI(SCRATCH1, (int32_t)inst.constant); - ADDW(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); - gpr.MarkGPRDirty(inst.dest, true); + ADDW(regs_.R(inst.dest), regs_.R(inst.src1), SCRATCH1); + regs_.MarkGPRDirty(inst.dest, true); } break; case IROp::SubConst: - gpr.Map(inst); + regs_.Map(inst); LI(SCRATCH1, (int32_t)inst.constant); - SUBW(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); - gpr.MarkGPRDirty(inst.dest, true); + SUBW(regs_.R(inst.dest), regs_.R(inst.src1), SCRATCH1); + regs_.MarkGPRDirty(inst.dest, true); break; case IROp::Neg: - gpr.Map(inst); - SUBW(gpr.R(inst.dest), R_ZERO, gpr.R(inst.src1)); - gpr.MarkGPRDirty(inst.dest, true); + regs_.Map(inst); + SUBW(regs_.R(inst.dest), R_ZERO, regs_.R(inst.src1)); + regs_.MarkGPRDirty(inst.dest, true); break; default: @@ -109,81 +109,81 @@ void RiscVJitBackend::CompIR_Logic(IRInst inst) { switch (inst.op) { case IROp::And: if (inst.src1 != inst.src2) { - gpr.Map(inst); - AND(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + regs_.Map(inst); + AND(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2)); } else if (inst.src1 != inst.dest) { - gpr.Map(inst); - MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); + regs_.Map(inst); + MV(regs_.R(inst.dest), regs_.R(inst.src1)); + regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1)); } break; case IROp::Or: if (inst.src1 != inst.src2) { // If both were normalized before, the result is normalized. - resultNormalized = gpr.IsNormalized32(inst.src1) && gpr.IsNormalized32(inst.src2); - gpr.Map(inst); - OR(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); - gpr.MarkGPRDirty(inst.dest, resultNormalized); + resultNormalized = regs_.IsNormalized32(inst.src1) && regs_.IsNormalized32(inst.src2); + regs_.Map(inst); + OR(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2)); + regs_.MarkGPRDirty(inst.dest, resultNormalized); } else if (inst.src1 != inst.dest) { - gpr.Map(inst); - MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); + regs_.Map(inst); + MV(regs_.R(inst.dest), regs_.R(inst.src1)); + regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1)); } break; case IROp::Xor: if (inst.src1 == inst.src2) { - gpr.SetGPRImm(inst.dest, 0); + regs_.SetGPRImm(inst.dest, 0); } else { - gpr.Map(inst); - XOR(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); + regs_.Map(inst); + XOR(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2)); } break; case IROp::AndConst: - resultNormalized = gpr.IsNormalized32(inst.src1); - gpr.Map(inst); + resultNormalized = regs_.IsNormalized32(inst.src1); + regs_.Map(inst); if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { - ANDI(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant); + ANDI(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant); } else { LI(SCRATCH1, (int32_t)inst.constant); - AND(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); + AND(regs_.R(inst.dest), regs_.R(inst.src1), SCRATCH1); } // If the sign bits aren't cleared, and it was normalized before - it still is. if ((inst.constant & 0x80000000) != 0 && resultNormalized) - gpr.MarkGPRDirty(inst.dest, true); + regs_.MarkGPRDirty(inst.dest, true); // Otherwise, if we cleared the sign bits, it's naturally normalized. else if ((inst.constant & 0x80000000) == 0) - gpr.MarkGPRDirty(inst.dest, true); + regs_.MarkGPRDirty(inst.dest, true); break; case IROp::OrConst: - resultNormalized = gpr.IsNormalized32(inst.src1); - gpr.Map(inst); + resultNormalized = regs_.IsNormalized32(inst.src1); + regs_.Map(inst); if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { - ORI(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant); + ORI(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant); } else { LI(SCRATCH1, (int32_t)inst.constant); - OR(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); + OR(regs_.R(inst.dest), regs_.R(inst.src1), SCRATCH1); } // Since our constant is normalized, oring its bits in won't hurt normalization. - gpr.MarkGPRDirty(inst.dest, resultNormalized); + regs_.MarkGPRDirty(inst.dest, resultNormalized); break; case IROp::XorConst: - gpr.Map(inst); + regs_.Map(inst); if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { - XORI(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant); + XORI(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant); } else { LI(SCRATCH1, (int32_t)inst.constant); - XOR(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1); + XOR(regs_.R(inst.dest), regs_.R(inst.src1), SCRATCH1); } break; case IROp::Not: - gpr.Map(inst); - NOT(gpr.R(inst.dest), gpr.R(inst.src1)); + regs_.Map(inst); + NOT(regs_.R(inst.dest), regs_.R(inst.src1)); break; default: @@ -198,32 +198,32 @@ void RiscVJitBackend::CompIR_Assign(IRInst inst) { switch (inst.op) { case IROp::Mov: if (inst.dest != inst.src1) { - gpr.Map(inst); - MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); + regs_.Map(inst); + MV(regs_.R(inst.dest), regs_.R(inst.src1)); + regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1)); } break; case IROp::Ext8to32: - gpr.Map(inst); + regs_.Map(inst); if (cpu_info.RiscV_Zbb) { - SEXT_B(gpr.R(inst.dest), gpr.R(inst.src1)); + SEXT_B(regs_.R(inst.dest), regs_.R(inst.src1)); } else { - SLLI(gpr.R(inst.dest), gpr.R(inst.src1), 24); - SRAIW(gpr.R(inst.dest), gpr.R(inst.dest), 24); + SLLI(regs_.R(inst.dest), regs_.R(inst.src1), 24); + SRAIW(regs_.R(inst.dest), regs_.R(inst.dest), 24); } - gpr.MarkGPRDirty(inst.dest, true); + regs_.MarkGPRDirty(inst.dest, true); break; case IROp::Ext16to32: - gpr.Map(inst); + regs_.Map(inst); if (cpu_info.RiscV_Zbb) { - SEXT_H(gpr.R(inst.dest), gpr.R(inst.src1)); + SEXT_H(regs_.R(inst.dest), regs_.R(inst.src1)); } else { - SLLI(gpr.R(inst.dest), gpr.R(inst.src1), 16); - SRAIW(gpr.R(inst.dest), gpr.R(inst.dest), 16); + SLLI(regs_.R(inst.dest), regs_.R(inst.src1), 16); + SRAIW(regs_.R(inst.dest), regs_.R(inst.dest), 16); } - gpr.MarkGPRDirty(inst.dest, true); + regs_.MarkGPRDirty(inst.dest, true); break; default: @@ -238,37 +238,37 @@ void RiscVJitBackend::CompIR_Bits(IRInst inst) { switch (inst.op) { case IROp::ReverseBits: if (cpu_info.RiscV_Zbb) { - gpr.Map(inst); + regs_.Map(inst); // Start by reversing bytes (note: this puts in upper 32 of XLEN.) - REV8(gpr.R(inst.dest), gpr.R(inst.src1)); + REV8(regs_.R(inst.dest), regs_.R(inst.src1)); // Swap nibbles. LI(SCRATCH1, (s32)0xF0F0F0F0); - SRLI(SCRATCH2, gpr.R(inst.dest), XLEN - 32 - 4); + SRLI(SCRATCH2, regs_.R(inst.dest), XLEN - 32 - 4); AND(SCRATCH2, SCRATCH2, SCRATCH1); if (XLEN >= 64) - SRLI(gpr.R(inst.dest), gpr.R(inst.dest), XLEN - 28); + SRLI(regs_.R(inst.dest), regs_.R(inst.dest), XLEN - 28); else - SLLI(gpr.R(inst.dest), gpr.R(inst.dest), 4); + SLLI(regs_.R(inst.dest), regs_.R(inst.dest), 4); SRLIW(SCRATCH1, SCRATCH1, 4); - AND(gpr.R(inst.dest), gpr.R(inst.dest), SCRATCH1); - OR(gpr.R(inst.dest), gpr.R(inst.dest), SCRATCH2); + AND(regs_.R(inst.dest), regs_.R(inst.dest), SCRATCH1); + OR(regs_.R(inst.dest), regs_.R(inst.dest), SCRATCH2); // Now the consecutive pairs. LI(SCRATCH1, (s32)0x33333333); - SRLI(SCRATCH2, gpr.R(inst.dest), 2); + SRLI(SCRATCH2, regs_.R(inst.dest), 2); AND(SCRATCH2, SCRATCH2, SCRATCH1); - AND(gpr.R(inst.dest), gpr.R(inst.dest), SCRATCH1); - SLLIW(gpr.R(inst.dest), gpr.R(inst.dest), 2); - OR(gpr.R(inst.dest), gpr.R(inst.dest), SCRATCH2); + AND(regs_.R(inst.dest), regs_.R(inst.dest), SCRATCH1); + SLLIW(regs_.R(inst.dest), regs_.R(inst.dest), 2); + OR(regs_.R(inst.dest), regs_.R(inst.dest), SCRATCH2); // And finally the even and odd bits. LI(SCRATCH1, (s32)0x55555555); - SRLI(SCRATCH2, gpr.R(inst.dest), 1); + SRLI(SCRATCH2, regs_.R(inst.dest), 1); AND(SCRATCH2, SCRATCH2, SCRATCH1); - AND(gpr.R(inst.dest), gpr.R(inst.dest), SCRATCH1); - SLLIW(gpr.R(inst.dest), gpr.R(inst.dest), 1); - OR(gpr.R(inst.dest), gpr.R(inst.dest), SCRATCH2); + AND(regs_.R(inst.dest), regs_.R(inst.dest), SCRATCH1); + SLLIW(regs_.R(inst.dest), regs_.R(inst.dest), 1); + OR(regs_.R(inst.dest), regs_.R(inst.dest), SCRATCH2); } else { CompIR_Generic(inst); } @@ -280,12 +280,12 @@ void RiscVJitBackend::CompIR_Bits(IRInst inst) { case IROp::BSwap32: if (cpu_info.RiscV_Zbb) { - gpr.Map(inst); - REV8(gpr.R(inst.dest), gpr.R(inst.src1)); + regs_.Map(inst); + REV8(regs_.R(inst.dest), regs_.R(inst.src1)); if (XLEN >= 64) { // REV8 swaps the entire register, so get the 32 highest bits. - SRAI(gpr.R(inst.dest), gpr.R(inst.dest), XLEN - 32); - gpr.MarkGPRDirty(inst.dest, true); + SRAI(regs_.R(inst.dest), regs_.R(inst.dest), XLEN - 32); + regs_.MarkGPRDirty(inst.dest, true); } } else { CompIR_Generic(inst); @@ -294,10 +294,10 @@ void RiscVJitBackend::CompIR_Bits(IRInst inst) { case IROp::Clz: if (cpu_info.RiscV_Zbb) { - gpr.Map(inst); + regs_.Map(inst); // This even sets to 32 when zero, perfect. - CLZW(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkGPRDirty(inst.dest, true); + CLZW(regs_.R(inst.dest), regs_.R(inst.src1)); + regs_.MarkGPRDirty(inst.dest, true); } else { CompIR_Generic(inst); } @@ -314,28 +314,28 @@ void RiscVJitBackend::CompIR_Shift(IRInst inst) { switch (inst.op) { case IROp::Shl: - gpr.Map(inst); - SLLW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); - gpr.MarkGPRDirty(inst.dest, true); + regs_.Map(inst); + SLLW(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2)); + regs_.MarkGPRDirty(inst.dest, true); break; case IROp::Shr: - gpr.Map(inst); - SRLW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); - gpr.MarkGPRDirty(inst.dest, true); + regs_.Map(inst); + SRLW(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2)); + regs_.MarkGPRDirty(inst.dest, true); break; case IROp::Sar: - gpr.Map(inst); - SRAW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); - gpr.MarkGPRDirty(inst.dest, true); + regs_.Map(inst); + SRAW(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2)); + regs_.MarkGPRDirty(inst.dest, true); break; case IROp::Ror: if (cpu_info.RiscV_Zbb) { - gpr.Map(inst); - RORW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2)); - gpr.MarkGPRDirty(inst.dest, true); + regs_.Map(inst); + RORW(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2)); + regs_.MarkGPRDirty(inst.dest, true); } else { CompIR_Generic(inst); } @@ -344,67 +344,67 @@ void RiscVJitBackend::CompIR_Shift(IRInst inst) { case IROp::ShlImm: // Shouldn't happen, but let's be safe of any passes that modify the ops. if (inst.src2 >= 32) { - gpr.SetGPRImm(inst.dest, 0); + regs_.SetGPRImm(inst.dest, 0); } else if (inst.src2 == 0) { if (inst.dest != inst.src1) { - gpr.Map(inst); - MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); + regs_.Map(inst); + MV(regs_.R(inst.dest), regs_.R(inst.src1)); + regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1)); } } else { - gpr.Map(inst); - SLLIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2); - gpr.MarkGPRDirty(inst.dest, true); + regs_.Map(inst); + SLLIW(regs_.R(inst.dest), regs_.R(inst.src1), inst.src2); + regs_.MarkGPRDirty(inst.dest, true); } break; case IROp::ShrImm: // Shouldn't happen, but let's be safe of any passes that modify the ops. if (inst.src2 >= 32) { - gpr.SetGPRImm(inst.dest, 0); + regs_.SetGPRImm(inst.dest, 0); } else if (inst.src2 == 0) { if (inst.dest != inst.src1) { - gpr.Map(inst); - MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); + regs_.Map(inst); + MV(regs_.R(inst.dest), regs_.R(inst.src1)); + regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1)); } } else { - gpr.Map(inst); - SRLIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2); - gpr.MarkGPRDirty(inst.dest, true); + regs_.Map(inst); + SRLIW(regs_.R(inst.dest), regs_.R(inst.src1), inst.src2); + regs_.MarkGPRDirty(inst.dest, true); } break; case IROp::SarImm: // Shouldn't happen, but let's be safe of any passes that modify the ops. if (inst.src2 >= 32) { - gpr.Map(inst); - SRAIW(gpr.R(inst.dest), gpr.R(inst.src1), 31); - gpr.MarkGPRDirty(inst.dest, true); + regs_.Map(inst); + SRAIW(regs_.R(inst.dest), regs_.R(inst.src1), 31); + regs_.MarkGPRDirty(inst.dest, true); } else if (inst.src2 == 0) { if (inst.dest != inst.src1) { - gpr.Map(inst); - MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); + regs_.Map(inst); + MV(regs_.R(inst.dest), regs_.R(inst.src1)); + regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1)); } } else { - gpr.Map(inst); - SRAIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2); - gpr.MarkGPRDirty(inst.dest, true); + regs_.Map(inst); + SRAIW(regs_.R(inst.dest), regs_.R(inst.src1), inst.src2); + regs_.MarkGPRDirty(inst.dest, true); } break; case IROp::RorImm: if (inst.src2 == 0) { if (inst.dest != inst.src1) { - gpr.Map(inst); - MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); + regs_.Map(inst); + MV(regs_.R(inst.dest), regs_.R(inst.src1)); + regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1)); } } else if (cpu_info.RiscV_Zbb) { - gpr.Map(inst); - RORIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2 & 31); - gpr.MarkGPRDirty(inst.dest, true); + regs_.Map(inst); + RORIW(regs_.R(inst.dest), regs_.R(inst.src1), inst.src2 & 31); + regs_.MarkGPRDirty(inst.dest, true); } else { CompIR_Generic(inst); } @@ -423,58 +423,58 @@ void RiscVJitBackend::CompIR_Compare(IRInst inst) { RiscVReg rhs = INVALID_REG; switch (inst.op) { case IROp::Slt: - gpr.Map(inst); + regs_.Map(inst); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); - SLT(gpr.R(inst.dest), lhs, rhs); - gpr.MarkGPRDirty(inst.dest, true); + SLT(regs_.R(inst.dest), lhs, rhs); + regs_.MarkGPRDirty(inst.dest, true); break; case IROp::SltConst: if (inst.constant == 0) { // Basically, getting the sign bit. Let's shift instead. - gpr.Map(inst); - SRLIW(gpr.R(inst.dest), gpr.R(inst.src1), 31); - gpr.MarkGPRDirty(inst.dest, true); + regs_.Map(inst); + SRLIW(regs_.R(inst.dest), regs_.R(inst.src1), 31); + regs_.MarkGPRDirty(inst.dest, true); } else { - gpr.Map(inst); + regs_.Map(inst); NormalizeSrc1(inst, &lhs, SCRATCH1, false); if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { - SLTI(gpr.R(inst.dest), lhs, (int32_t)inst.constant); + SLTI(regs_.R(inst.dest), lhs, (int32_t)inst.constant); } else { LI(SCRATCH2, (int32_t)inst.constant); - SLT(gpr.R(inst.dest), lhs, SCRATCH2); + SLT(regs_.R(inst.dest), lhs, SCRATCH2); } - gpr.MarkGPRDirty(inst.dest, true); + regs_.MarkGPRDirty(inst.dest, true); } break; case IROp::SltU: - gpr.Map(inst); + regs_.Map(inst); // It's still fine to sign extend, the biggest just get even bigger. NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); - SLTU(gpr.R(inst.dest), lhs, rhs); - gpr.MarkGPRDirty(inst.dest, true); + SLTU(regs_.R(inst.dest), lhs, rhs); + regs_.MarkGPRDirty(inst.dest, true); break; case IROp::SltUConst: if (inst.constant == 0) { - gpr.SetGPRImm(inst.dest, 0); + regs_.SetGPRImm(inst.dest, 0); } else { - gpr.Map(inst); + regs_.Map(inst); NormalizeSrc1(inst, &lhs, SCRATCH1, false); // We sign extend because we're comparing against something normalized. // It's also the most efficient to set. if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) { - SLTIU(gpr.R(inst.dest), lhs, (int32_t)inst.constant); + SLTIU(regs_.R(inst.dest), lhs, (int32_t)inst.constant); } else { LI(SCRATCH2, (int32_t)inst.constant); - SLTU(gpr.R(inst.dest), lhs, SCRATCH2); + SLTU(regs_.R(inst.dest), lhs, SCRATCH2); } - gpr.MarkGPRDirty(inst.dest, true); + regs_.MarkGPRDirty(inst.dest, true); } break; @@ -497,7 +497,7 @@ void RiscVJitBackend::CompIR_CondAssign(IRInst inst) { return; // We could have a "zero" with wrong upper due to XOR, so we have to normalize. - gpr.Map(inst); + regs_.Map(inst); NormalizeSrc1(inst, &lhs, SCRATCH1, true); switch (inst.op) { @@ -512,43 +512,43 @@ void RiscVJitBackend::CompIR_CondAssign(IRInst inst) { break; } - MV(gpr.R(inst.dest), gpr.R(inst.src2)); + MV(regs_.R(inst.dest), regs_.R(inst.src2)); SetJumpTarget(fixup); break; case IROp::Max: if (inst.src1 != inst.src2) { if (cpu_info.RiscV_Zbb) { - gpr.Map(inst); + regs_.Map(inst); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); - MAX(gpr.R(inst.dest), lhs, rhs); + MAX(regs_.R(inst.dest), lhs, rhs); // Because we had to normalize the inputs, the output is normalized. - gpr.MarkGPRDirty(inst.dest, true); + regs_.MarkGPRDirty(inst.dest, true); } else { CompIR_Generic(inst); } } else if (inst.dest != inst.src1) { - gpr.Map(inst); - MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); + regs_.Map(inst); + MV(regs_.R(inst.dest), regs_.R(inst.src1)); + regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1)); } break; case IROp::Min: if (inst.src1 != inst.src2) { if (cpu_info.RiscV_Zbb) { - gpr.Map(inst); + regs_.Map(inst); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); - MIN(gpr.R(inst.dest), lhs, rhs); + MIN(regs_.R(inst.dest), lhs, rhs); // Because we had to normalize the inputs, the output is normalized. - gpr.MarkGPRDirty(inst.dest, true); + regs_.MarkGPRDirty(inst.dest, true); } else { CompIR_Generic(inst); } } else if (inst.dest != inst.src1) { - gpr.Map(inst); - MV(gpr.R(inst.dest), gpr.R(inst.src1)); - gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1)); + regs_.Map(inst); + MV(regs_.R(inst.dest), regs_.R(inst.src1)); + regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1)); } break; @@ -563,27 +563,27 @@ void RiscVJitBackend::CompIR_HiLo(IRInst inst) { switch (inst.op) { case IROp::MtLo: - gpr.MapDirtyIn(IRREG_LO, inst.src1); - MV(gpr.R(IRREG_LO), gpr.R(inst.src1)); - gpr.MarkGPRDirty(IRREG_LO, gpr.IsNormalized32(inst.src1)); + regs_.MapGPRDirtyIn(IRREG_LO, inst.src1); + MV(regs_.R(IRREG_LO), regs_.R(inst.src1)); + regs_.MarkGPRDirty(IRREG_LO, regs_.IsNormalized32(inst.src1)); break; case IROp::MtHi: - gpr.MapDirtyIn(IRREG_HI, inst.src1); - MV(gpr.R(IRREG_HI), gpr.R(inst.src1)); - gpr.MarkGPRDirty(IRREG_HI, gpr.IsNormalized32(inst.src1)); + regs_.MapGPRDirtyIn(IRREG_HI, inst.src1); + MV(regs_.R(IRREG_HI), regs_.R(inst.src1)); + regs_.MarkGPRDirty(IRREG_HI, regs_.IsNormalized32(inst.src1)); break; case IROp::MfLo: - gpr.MapDirtyIn(inst.dest, IRREG_LO); - MV(gpr.R(inst.dest), gpr.R(IRREG_LO)); - gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(IRREG_LO)); + regs_.MapGPRDirtyIn(inst.dest, IRREG_LO); + MV(regs_.R(inst.dest), regs_.R(IRREG_LO)); + regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_LO)); break; case IROp::MfHi: - gpr.MapDirtyIn(inst.dest, IRREG_HI); - MV(gpr.R(inst.dest), gpr.R(IRREG_HI)); - gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(IRREG_HI)); + regs_.MapGPRDirtyIn(inst.dest, IRREG_HI); + MV(regs_.R(inst.dest), regs_.R(IRREG_HI)); + regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_HI)); break; default: @@ -597,12 +597,12 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) { auto makeArgsUnsigned = [&](RiscVReg *lhs, RiscVReg *rhs) { if (cpu_info.RiscV_Zba) { - ZEXT_W(SCRATCH1, gpr.R(inst.src1)); - ZEXT_W(SCRATCH2, gpr.R(inst.src2)); + ZEXT_W(SCRATCH1, regs_.R(inst.src1)); + ZEXT_W(SCRATCH2, regs_.R(inst.src2)); } else { - SLLI(SCRATCH1, gpr.R(inst.src1), XLEN - 32); + SLLI(SCRATCH1, regs_.R(inst.src1), XLEN - 32); SRLI(SCRATCH1, SCRATCH1, XLEN - 32); - SLLI(SCRATCH2, gpr.R(inst.src2), XLEN - 32); + SLLI(SCRATCH2, regs_.R(inst.src2), XLEN - 32); SRLI(SCRATCH2, SCRATCH2, XLEN - 32); } *lhs = SCRATCH1; @@ -611,17 +611,17 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) { auto combinePrevMulResult = [&] { // TODO: Using a single reg for HI/LO would make this less ugly. if (cpu_info.RiscV_Zba) { - ZEXT_W(gpr.R(IRREG_LO), gpr.R(IRREG_LO)); + ZEXT_W(regs_.R(IRREG_LO), regs_.R(IRREG_LO)); } else { - SLLI(gpr.R(IRREG_LO), gpr.R(IRREG_LO), XLEN - 32); - SRLI(gpr.R(IRREG_LO), gpr.R(IRREG_LO), XLEN - 32); + SLLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), XLEN - 32); + SRLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), XLEN - 32); } - SLLI(gpr.R(IRREG_HI), gpr.R(IRREG_HI), 32); - OR(gpr.R(IRREG_LO), gpr.R(IRREG_LO), gpr.R(IRREG_HI)); + SLLI(regs_.R(IRREG_HI), regs_.R(IRREG_HI), 32); + OR(regs_.R(IRREG_LO), regs_.R(IRREG_LO), regs_.R(IRREG_HI)); }; auto splitMulResult = [&] { - SRAI(gpr.R(IRREG_HI), gpr.R(IRREG_LO), 32); - gpr.MarkGPRDirty(IRREG_HI, true); + SRAI(regs_.R(IRREG_HI), regs_.R(IRREG_LO), 32); + regs_.MarkGPRDirty(IRREG_HI, true); }; RiscVReg lhs = INVALID_REG; @@ -630,58 +630,58 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) { case IROp::Mult: // TODO: Maybe IR could simplify when HI is not needed or clobbered? // TODO: HI/LO merge optimization? Have to be careful of passes that split them... - gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2); + regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); - MUL(gpr.R(IRREG_LO), lhs, rhs); + MUL(regs_.R(IRREG_LO), lhs, rhs); splitMulResult(); break; case IROp::MultU: // This is an "anti-norm32" case. Let's just zero always. // TODO: If we could know that LO was only needed, we could use MULW and be done. - gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2); + regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2); makeArgsUnsigned(&lhs, &rhs); - MUL(gpr.R(IRREG_LO), lhs, rhs); + MUL(regs_.R(IRREG_LO), lhs, rhs); splitMulResult(); break; case IROp::Madd: - gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); + regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); MUL(SCRATCH1, lhs, rhs); combinePrevMulResult(); - ADD(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1); + ADD(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1); splitMulResult(); break; case IROp::MaddU: - gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); + regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); makeArgsUnsigned(&lhs, &rhs); MUL(SCRATCH1, lhs, rhs); combinePrevMulResult(); - ADD(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1); + ADD(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1); splitMulResult(); break; case IROp::Msub: - gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); + regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); MUL(SCRATCH1, lhs, rhs); combinePrevMulResult(); - SUB(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1); + SUB(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1); splitMulResult(); break; case IROp::MsubU: - gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); + regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); makeArgsUnsigned(&lhs, &rhs); MUL(SCRATCH1, lhs, rhs); combinePrevMulResult(); - SUB(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1); + SUB(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1); splitMulResult(); break; @@ -697,18 +697,18 @@ void RiscVJitBackend::CompIR_Div(IRInst inst) { RiscVReg numReg, denomReg; switch (inst.op) { case IROp::Div: - gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); // We have to do this because of the divide by zero and overflow checks below. NormalizeSrc12(inst, &numReg, &denomReg, SCRATCH1, SCRATCH2, true); - DIVW(gpr.R(IRREG_LO), numReg, denomReg); - REMW(gpr.R(IRREG_HI), numReg, denomReg); + DIVW(regs_.R(IRREG_LO), numReg, denomReg); + REMW(regs_.R(IRREG_HI), numReg, denomReg); // Now some tweaks for divide by zero and overflow. { // Start with divide by zero, remainder is fine. FixupBranch skipNonZero = BNE(denomReg, R_ZERO); FixupBranch keepNegOne = BGE(numReg, R_ZERO); - LI(gpr.R(IRREG_LO), 1); + LI(regs_.R(IRREG_LO), 1); SetJumpTarget(keepNegOne); SetJumpTarget(skipNonZero); @@ -718,18 +718,18 @@ void RiscVJitBackend::CompIR_Div(IRInst inst) { FixupBranch notMostNegative = BNE(numReg, R_RA); LI(R_RA, -1); FixupBranch notNegativeOne = BNE(denomReg, R_RA); - LI(gpr.R(IRREG_HI), -1); + LI(regs_.R(IRREG_HI), -1); SetJumpTarget(notNegativeOne); SetJumpTarget(notMostNegative); } break; case IROp::DivU: - gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); // We have to do this because of the divide by zero check below. NormalizeSrc12(inst, &numReg, &denomReg, SCRATCH1, SCRATCH2, true); - DIVUW(gpr.R(IRREG_LO), numReg, denomReg); - REMUW(gpr.R(IRREG_HI), numReg, denomReg); + DIVUW(regs_.R(IRREG_LO), numReg, denomReg); + REMUW(regs_.R(IRREG_HI), numReg, denomReg); // On divide by zero, everything is correct already except the 0xFFFF case. { @@ -737,7 +737,7 @@ void RiscVJitBackend::CompIR_Div(IRInst inst) { // Luckily, we don't need SCRATCH2/denomReg anymore. LI(SCRATCH2, 0xFFFF); FixupBranch keepNegOne = BLTU(SCRATCH2, numReg); - MV(gpr.R(IRREG_LO), SCRATCH2); + MV(regs_.R(IRREG_LO), SCRATCH2); SetJumpTarget(keepNegOne); SetJumpTarget(skipNonZero); } diff --git a/Core/MIPS/RiscV/RiscVCompBranch.cpp b/Core/MIPS/RiscV/RiscVCompBranch.cpp index f146fdd1ee..f1126b0317 100644 --- a/Core/MIPS/RiscV/RiscVCompBranch.cpp +++ b/Core/MIPS/RiscV/RiscVCompBranch.cpp @@ -45,7 +45,7 @@ void RiscVJitBackend::CompIR_Exit(IRInst inst) { break; case IROp::ExitToReg: - exitReg = gpr.MapGPR(inst.src1); + exitReg = regs_.MapGPR(inst.src1); FlushAll(); // TODO: If ever we don't read this back in dispatcherPCInSCRATCH1_, we should zero upper. MV(SCRATCH1, exitReg); @@ -72,7 +72,7 @@ void RiscVJitBackend::CompIR_ExitIf(IRInst inst) { switch (inst.op) { case IROp::ExitToConstIfEq: case IROp::ExitToConstIfNeq: - gpr.Map(inst); + regs_.Map(inst); // We can't use SCRATCH1, which is destroyed by FlushAll()... but cheat and use R_RA. NormalizeSrc12(inst, &lhs, &rhs, R_RA, SCRATCH2, true); FlushAll(); @@ -99,7 +99,7 @@ void RiscVJitBackend::CompIR_ExitIf(IRInst inst) { case IROp::ExitToConstIfGeZ: case IROp::ExitToConstIfLtZ: case IROp::ExitToConstIfLeZ: - gpr.Map(inst); + regs_.Map(inst); NormalizeSrc1(inst, &lhs, SCRATCH2, true); FlushAll(); diff --git a/Core/MIPS/RiscV/RiscVCompFPU.cpp b/Core/MIPS/RiscV/RiscVCompFPU.cpp index 5e668ce907..bdc2def130 100644 --- a/Core/MIPS/RiscV/RiscVCompFPU.cpp +++ b/Core/MIPS/RiscV/RiscVCompFPU.cpp @@ -39,35 +39,35 @@ void RiscVJitBackend::CompIR_FArith(IRInst inst) { switch (inst.op) { case IROp::FAdd: - fpr.Map(inst); - FADD(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + regs_.Map(inst); + FADD(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2)); break; case IROp::FSub: - fpr.Map(inst); - FSUB(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + regs_.Map(inst); + FSUB(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2)); break; case IROp::FMul: - fpr.Map(inst); + regs_.Map(inst); // We'll assume everyone will make it such that 0 * infinity = NAN properly. // See blame on this comment if that proves untrue. - FMUL(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + FMUL(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2)); break; case IROp::FDiv: - fpr.Map(inst); - FDIV(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + regs_.Map(inst); + FDIV(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2)); break; case IROp::FSqrt: - fpr.Map(inst); - FSQRT(32, fpr.R(inst.dest), fpr.R(inst.src1)); + regs_.Map(inst); + FSQRT(32, regs_.F(inst.dest), regs_.F(inst.src1)); break; case IROp::FNeg: - fpr.Map(inst); - FNEG(32, fpr.R(inst.dest), fpr.R(inst.src1)); + regs_.Map(inst); + FNEG(32, regs_.F(inst.dest), regs_.F(inst.src1)); break; default: @@ -83,9 +83,9 @@ void RiscVJitBackend::CompIR_FCondAssign(IRInst inst) { bool maxCondition = inst.op == IROp::FMax; // FMin and FMax are used by VFPU and handle NAN/INF as just a larger exponent. - fpr.Map(inst); - FCLASS(32, SCRATCH1, fpr.R(inst.src1)); - FCLASS(32, SCRATCH2, fpr.R(inst.src2)); + regs_.Map(inst); + FCLASS(32, SCRATCH1, regs_.F(inst.src1)); + FCLASS(32, SCRATCH2, regs_.F(inst.src2)); // If either side is a NAN, it needs to participate in the comparison. OR(SCRATCH1, SCRATCH1, SCRATCH2); @@ -94,8 +94,8 @@ void RiscVJitBackend::CompIR_FCondAssign(IRInst inst) { FixupBranch useNormalCond = BEQ(SCRATCH1, R_ZERO); // Time to use bits... classify won't help because it ignores -NAN. - FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1)); - FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src2)); + FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1)); + FMV(FMv::X, FMv::W, SCRATCH2, regs_.F(inst.src2)); // If both are negative, we flip the comparison (not two's compliment.) // We cheat and use RA... @@ -116,7 +116,7 @@ void RiscVJitBackend::CompIR_FCondAssign(IRInst inst) { MAX(SCRATCH1, SCRATCH1, SCRATCH2); SetJumpTarget(skipSwapCompare); } else { - RiscVReg isSrc1LowerReg = gpr.GetAndLockTempR(); + RiscVReg isSrc1LowerReg = regs_.GetAndLockTempR(); SLT(isSrc1LowerReg, SCRATCH1, SCRATCH2); // Flip the flag (to reverse the min/max) based on if both were negative. XOR(isSrc1LowerReg, isSrc1LowerReg, R_RA); @@ -129,14 +129,14 @@ void RiscVJitBackend::CompIR_FCondAssign(IRInst inst) { SetJumpTarget(useSrc1); } - FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1); FixupBranch finish = J(); SetJumpTarget(useNormalCond); if (maxCondition) - FMAX(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + FMAX(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2)); else - FMIN(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + FMIN(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2)); SetJumpTarget(finish); } @@ -146,21 +146,21 @@ void RiscVJitBackend::CompIR_FAssign(IRInst inst) { switch (inst.op) { case IROp::FMov: if (inst.dest != inst.src1) { - fpr.Map(inst); - FMV(32, fpr.R(inst.dest), fpr.R(inst.src1)); + regs_.Map(inst); + FMV(32, regs_.F(inst.dest), regs_.F(inst.src1)); } break; case IROp::FAbs: - fpr.Map(inst); - FABS(32, fpr.R(inst.dest), fpr.R(inst.src1)); + regs_.Map(inst); + FABS(32, regs_.F(inst.dest), regs_.F(inst.src1)); break; case IROp::FSign: { - fpr.Map(inst); + regs_.Map(inst); // Check if it's negative zero, either 0x10/0x08 is zero. - FCLASS(32, SCRATCH1, fpr.R(inst.src1)); + FCLASS(32, SCRATCH1, regs_.F(inst.src1)); ANDI(SCRATCH1, SCRATCH1, 0x18); SEQZ(SCRATCH1, SCRATCH1); // Okay, it's zero if zero, 1 otherwise. Convert 1 to a constant 1.0. @@ -169,14 +169,14 @@ void RiscVJitBackend::CompIR_FAssign(IRInst inst) { LI(SCRATCH1, 1.0f); // Now we just need the sign from it. - FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1)); + FMV(FMv::X, FMv::W, SCRATCH2, regs_.F(inst.src1)); // Use a wall to isolate the sign, and combine. SRAIW(SCRATCH2, SCRATCH2, 31); SLLIW(SCRATCH2, SCRATCH2, 31); OR(SCRATCH1, SCRATCH1, SCRATCH2); SetJumpTarget(skipOne); - FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1); break; } @@ -190,23 +190,23 @@ void RiscVJitBackend::CompIR_FRound(IRInst inst) { CONDITIONAL_DISABLE; // TODO: If this is followed by a GPR transfer, might want to combine. - fpr.Map(inst); + regs_.Map(inst); switch (inst.op) { case IROp::FRound: - FCVT(FConv::W, FConv::S, SCRATCH1, fpr.R(inst.src1), Round::NEAREST_EVEN); + FCVT(FConv::W, FConv::S, SCRATCH1, regs_.F(inst.src1), Round::NEAREST_EVEN); break; case IROp::FTrunc: - FCVT(FConv::W, FConv::S, SCRATCH1, fpr.R(inst.src1), Round::TOZERO); + FCVT(FConv::W, FConv::S, SCRATCH1, regs_.F(inst.src1), Round::TOZERO); break; case IROp::FCeil: - FCVT(FConv::W, FConv::S, SCRATCH1, fpr.R(inst.src1), Round::UP); + FCVT(FConv::W, FConv::S, SCRATCH1, regs_.F(inst.src1), Round::UP); break; case IROp::FFloor: - FCVT(FConv::W, FConv::S, SCRATCH1, fpr.R(inst.src1), Round::DOWN); + FCVT(FConv::W, FConv::S, SCRATCH1, regs_.F(inst.src1), Round::DOWN); break; default: @@ -214,7 +214,7 @@ void RiscVJitBackend::CompIR_FRound(IRInst inst) { break; } - FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1); } void RiscVJitBackend::CompIR_FCvt(IRInst inst) { @@ -228,9 +228,9 @@ void RiscVJitBackend::CompIR_FCvt(IRInst inst) { case IROp::FCvtSW: // TODO: This is probably proceeded by a GPR transfer, might be ideal to combine. - fpr.Map(inst); - FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1)); - FCVT(FConv::S, FConv::W, fpr.R(inst.dest), SCRATCH1); + regs_.Map(inst); + FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1)); + FCVT(FConv::S, FConv::W, regs_.F(inst.dest), SCRATCH1); break; case IROp::FCvtScaledWS: @@ -243,17 +243,17 @@ void RiscVJitBackend::CompIR_FCvt(IRInst inst) { case 3: rm = Round::DOWN; break; } - tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1); + tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1); // Prepare the double src1 and the multiplier. - FCVT(FConv::D, FConv::S, fpr.R(inst.dest), fpr.R(inst.src1)); + FCVT(FConv::D, FConv::S, regs_.F(inst.dest), regs_.F(inst.src1)); LI(SCRATCH1, 1UL << (inst.src2 & 0x1F)); FCVT(FConv::D, FConv::WU, tempReg, SCRATCH1, rm); - FMUL(64, fpr.R(inst.dest), fpr.R(inst.dest), tempReg, rm); + FMUL(64, regs_.F(inst.dest), regs_.F(inst.dest), tempReg, rm); // NAN and clamping should all be correct. - FCVT(FConv::W, FConv::D, SCRATCH1, fpr.R(inst.dest), rm); + FCVT(FConv::W, FConv::D, SCRATCH1, regs_.F(inst.dest), rm); // TODO: Could combine with a transfer, often is one... - FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1); } else { CompIR_Generic(inst); } @@ -261,14 +261,14 @@ void RiscVJitBackend::CompIR_FCvt(IRInst inst) { case IROp::FCvtScaledSW: // TODO: This is probably proceeded by a GPR transfer, might be ideal to combine. - tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1); - FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1)); - FCVT(FConv::S, FConv::W, fpr.R(inst.dest), SCRATCH1); + tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1); + FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1)); + FCVT(FConv::S, FConv::W, regs_.F(inst.dest), SCRATCH1); // Pre-divide so we can avoid any actual divide. LI(SCRATCH1, 1.0f / (1UL << (inst.src2 & 0x1F))); FMV(FMv::W, FMv::X, tempReg, SCRATCH1); - FMUL(32, fpr.R(inst.dest), fpr.R(inst.dest), tempReg); + FMUL(32, regs_.F(inst.dest), regs_.F(inst.dest), tempReg); break; default: @@ -286,48 +286,48 @@ void RiscVJitBackend::CompIR_FSat(IRInst inst) { FixupBranch skipHigher; switch (inst.op) { case IROp::FSat0_1: - tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1); + tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1); if (inst.dest != inst.src1) - FMV(32, fpr.R(inst.dest), fpr.R(inst.src1)); + FMV(32, regs_.F(inst.dest), regs_.F(inst.src1)); // First, set SCRATCH1 = clamp to zero, SCRATCH2 = clamp to one. FCVT(FConv::S, FConv::W, tempReg, R_ZERO); // FLE here is intentional to convert -0.0 to +0.0. - FLE(32, SCRATCH1, fpr.R(inst.src1), tempReg); + FLE(32, SCRATCH1, regs_.F(inst.src1), tempReg); LI(SCRATCH2, 1.0f); FMV(FMv::W, FMv::X, tempReg, SCRATCH2); - FLT(32, SCRATCH2, tempReg, fpr.R(inst.src1)); + FLT(32, SCRATCH2, tempReg, regs_.F(inst.src1)); skipLower = BEQ(SCRATCH1, R_ZERO); - FCVT(FConv::S, FConv::W, fpr.R(inst.dest), R_ZERO); + FCVT(FConv::S, FConv::W, regs_.F(inst.dest), R_ZERO); finishLower = J(); SetJumpTarget(skipLower); skipHigher = BEQ(SCRATCH2, R_ZERO); // Still has 1.0 in it. - FMV(32, fpr.R(inst.dest), tempReg); + FMV(32, regs_.F(inst.dest), tempReg); SetJumpTarget(finishLower); SetJumpTarget(skipHigher); break; case IROp::FSatMinus1_1: - tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1); + tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1); if (inst.dest != inst.src1) - FMV(32, fpr.R(inst.dest), fpr.R(inst.src1)); + FMV(32, regs_.F(inst.dest), regs_.F(inst.src1)); // First, set SCRATCH1 = clamp to negative, SCRATCH2 = clamp to positive. LI(SCRATCH2, -1.0f); FMV(FMv::W, FMv::X, tempReg, SCRATCH2); - FLT(32, SCRATCH1, fpr.R(inst.src1), tempReg); + FLT(32, SCRATCH1, regs_.F(inst.src1), tempReg); FNEG(32, tempReg, tempReg); - FLT(32, SCRATCH2, tempReg, fpr.R(inst.src1)); + FLT(32, SCRATCH2, tempReg, regs_.F(inst.src1)); // But we can actually do one branch, using sign-injection to keep the original sign. OR(SCRATCH1, SCRATCH1, SCRATCH2); skipLower = BEQ(SCRATCH1, R_ZERO); - FSGNJ(32, fpr.R(inst.dest), tempReg, fpr.R(inst.dest)); + FSGNJ(32, regs_.F(inst.dest), tempReg, regs_.F(inst.dest)); SetJumpTarget(skipLower); break; @@ -346,134 +346,134 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { case IROp::FCmp: switch (inst.dest) { case IRFpCompareMode::False: - gpr.SetGPRImm(IRREG_FPCOND, 0); + regs_.SetGPRImm(IRREG_FPCOND, 0); break; case IRFpCompareMode::EitherUnordered: - fpr.Map(inst); - gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - FCLASS(32, SCRATCH1, fpr.R(inst.src1)); - FCLASS(32, SCRATCH2, fpr.R(inst.src2)); + regs_.Map(inst); + regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + FCLASS(32, SCRATCH1, regs_.F(inst.src1)); + FCLASS(32, SCRATCH2, regs_.F(inst.src2)); OR(SCRATCH1, SCRATCH1, SCRATCH2); // NAN is 0x100 or 0x200. ANDI(SCRATCH1, SCRATCH1, 0x300); - SNEZ(gpr.R(IRREG_FPCOND), SCRATCH1); + SNEZ(regs_.R(IRREG_FPCOND), SCRATCH1); break; case IRFpCompareMode::EqualOrdered: - fpr.Map(inst); - gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - FEQ(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2)); + regs_.Map(inst); + regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + FEQ(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2)); break; case IRFpCompareMode::EqualUnordered: - fpr.Map(inst); - gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - FEQ(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2)); + regs_.Map(inst); + regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + FEQ(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2)); // Now let's just OR in the unordered check. - FCLASS(32, SCRATCH1, fpr.R(inst.src1)); - FCLASS(32, SCRATCH2, fpr.R(inst.src2)); + FCLASS(32, SCRATCH1, regs_.F(inst.src1)); + FCLASS(32, SCRATCH2, regs_.F(inst.src2)); OR(SCRATCH1, SCRATCH1, SCRATCH2); // NAN is 0x100 or 0x200. ANDI(SCRATCH1, SCRATCH1, 0x300); SNEZ(SCRATCH1, SCRATCH1); - OR(gpr.R(IRREG_FPCOND), gpr.R(IRREG_FPCOND), SCRATCH1); + OR(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND), SCRATCH1); break; case IRFpCompareMode::LessEqualOrdered: - fpr.Map(inst); - gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - FLE(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2)); + regs_.Map(inst); + regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + FLE(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2)); break; case IRFpCompareMode::LessEqualUnordered: - fpr.Map(inst); - gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - FLT(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src2), fpr.R(inst.src1)); - SEQZ(gpr.R(IRREG_FPCOND), gpr.R(IRREG_FPCOND)); + regs_.Map(inst); + regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + FLT(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src2), regs_.F(inst.src1)); + SEQZ(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND)); break; case IRFpCompareMode::LessOrdered: - fpr.Map(inst); - gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - FLT(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2)); + regs_.Map(inst); + regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + FLT(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2)); break; case IRFpCompareMode::LessUnordered: - fpr.Map(inst); - gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - FLE(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src2), fpr.R(inst.src1)); - SEQZ(gpr.R(IRREG_FPCOND), gpr.R(IRREG_FPCOND)); + regs_.Map(inst); + regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + FLE(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src2), regs_.F(inst.src1)); + SEQZ(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND)); break; } break; case IROp::FCmovVfpuCC: - gpr.MapGPR(IRREG_VFPU_CC); - fpr.Map(inst); + regs_.MapGPR(IRREG_VFPU_CC); + regs_.Map(inst); if ((inst.src2 & 0xF) == 0) { - ANDI(SCRATCH1, gpr.R(IRREG_VFPU_CC), 1); + ANDI(SCRATCH1, regs_.R(IRREG_VFPU_CC), 1); } else if (cpu_info.RiscV_Zbs) { - BEXTI(SCRATCH1, gpr.R(IRREG_VFPU_CC), inst.src2 & 0xF); + BEXTI(SCRATCH1, regs_.R(IRREG_VFPU_CC), inst.src2 & 0xF); } else { - SRLI(SCRATCH1, gpr.R(IRREG_VFPU_CC), inst.src2 & 0xF); + SRLI(SCRATCH1, regs_.R(IRREG_VFPU_CC), inst.src2 & 0xF); ANDI(SCRATCH1, SCRATCH1, 1); } if ((inst.src2 >> 7) & 1) { FixupBranch skip = BEQ(SCRATCH1, R_ZERO); - FMV(32, fpr.R(inst.dest), fpr.R(inst.src1)); + FMV(32, regs_.F(inst.dest), regs_.F(inst.src1)); SetJumpTarget(skip); } else { FixupBranch skip = BNE(SCRATCH1, R_ZERO); - FMV(32, fpr.R(inst.dest), fpr.R(inst.src1)); + FMV(32, regs_.F(inst.dest), regs_.F(inst.src1)); SetJumpTarget(skip); } break; case IROp::FCmpVfpuBit: - gpr.MapGPR(IRREG_VFPU_CC, MIPSMap::DIRTY); + regs_.MapGPR(IRREG_VFPU_CC, MIPSMap::DIRTY); switch (VCondition(inst.dest & 0xF)) { case VC_EQ: - fpr.Map(inst); - FEQ(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2)); + regs_.Map(inst); + FEQ(32, SCRATCH1, regs_.F(inst.src1), regs_.F(inst.src2)); break; case VC_NE: - fpr.Map(inst); + regs_.Map(inst); // We could almost negate FEQ, except NAN != NAN. // Anything != NAN is false and NAN != NAN is within that, so we only check one side. - FCLASS(32, SCRATCH2, fpr.R(inst.src2)); + FCLASS(32, SCRATCH2, regs_.F(inst.src2)); // NAN is 0x100 or 0x200. ANDI(SCRATCH2, SCRATCH2, 0x300); SNEZ(SCRATCH2, SCRATCH2); - FEQ(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2)); + FEQ(32, SCRATCH1, regs_.F(inst.src1), regs_.F(inst.src2)); SEQZ(SCRATCH1, SCRATCH1); // Just OR in whether that side was a NAN so it's always not equal. OR(SCRATCH1, SCRATCH1, SCRATCH2); break; case VC_LT: - fpr.Map(inst); - FLT(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2)); + regs_.Map(inst); + FLT(32, SCRATCH1, regs_.F(inst.src1), regs_.F(inst.src2)); break; case VC_LE: - fpr.Map(inst); - FLE(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2)); + regs_.Map(inst); + FLE(32, SCRATCH1, regs_.F(inst.src1), regs_.F(inst.src2)); break; case VC_GT: - fpr.Map(inst); - FLT(32, SCRATCH1, fpr.R(inst.src2), fpr.R(inst.src1)); + regs_.Map(inst); + FLT(32, SCRATCH1, regs_.F(inst.src2), regs_.F(inst.src1)); break; case VC_GE: - fpr.Map(inst); - FLE(32, SCRATCH1, fpr.R(inst.src2), fpr.R(inst.src1)); + regs_.Map(inst); + FLE(32, SCRATCH1, regs_.F(inst.src2), regs_.F(inst.src1)); break; case VC_EZ: case VC_NZ: - fpr.MapFPR(inst.src1); + regs_.MapFPR(inst.src1); // Zero is either 0x10 or 0x08. - FCLASS(32, SCRATCH1, fpr.R(inst.src1)); + FCLASS(32, SCRATCH1, regs_.F(inst.src1)); ANDI(SCRATCH1, SCRATCH1, 0x18); if ((inst.dest & 4) == 0) SNEZ(SCRATCH1, SCRATCH1); @@ -482,9 +482,9 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case VC_EN: case VC_NN: - fpr.MapFPR(inst.src1); + regs_.MapFPR(inst.src1); // NAN is either 0x100 or 0x200. - FCLASS(32, SCRATCH1, fpr.R(inst.src1)); + FCLASS(32, SCRATCH1, regs_.F(inst.src1)); ANDI(SCRATCH1, SCRATCH1, 0x300); if ((inst.dest & 4) == 0) SNEZ(SCRATCH1, SCRATCH1); @@ -493,9 +493,9 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case VC_EI: case VC_NI: - fpr.MapFPR(inst.src1); + regs_.MapFPR(inst.src1); // Infinity is either 0x80 or 0x01. - FCLASS(32, SCRATCH1, fpr.R(inst.src1)); + FCLASS(32, SCRATCH1, regs_.F(inst.src1)); ANDI(SCRATCH1, SCRATCH1, 0x81); if ((inst.dest & 4) == 0) SNEZ(SCRATCH1, SCRATCH1); @@ -504,9 +504,9 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case VC_ES: case VC_NS: - fpr.MapFPR(inst.src1); + regs_.MapFPR(inst.src1); // Infinity is either 0x80 or 0x01, NAN is either 0x100 or 0x200. - FCLASS(32, SCRATCH1, fpr.R(inst.src1)); + FCLASS(32, SCRATCH1, regs_.F(inst.src1)); ANDI(SCRATCH1, SCRATCH1, 0x381); if ((inst.dest & 4) == 0) SNEZ(SCRATCH1, SCRATCH1); @@ -521,15 +521,15 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; } - ANDI(gpr.R(IRREG_VFPU_CC), gpr.R(IRREG_VFPU_CC), ~(1 << (inst.dest >> 4))); + ANDI(regs_.R(IRREG_VFPU_CC), regs_.R(IRREG_VFPU_CC), ~(1 << (inst.dest >> 4))); if ((inst.dest >> 4) != 0) SLLI(SCRATCH1, SCRATCH1, inst.dest >> 4); - OR(gpr.R(IRREG_VFPU_CC), gpr.R(IRREG_VFPU_CC), SCRATCH1); + OR(regs_.R(IRREG_VFPU_CC), regs_.R(IRREG_VFPU_CC), SCRATCH1); break; case IROp::FCmpVfpuAggregate: - gpr.MapGPR(IRREG_VFPU_CC, MIPSMap::DIRTY); - ANDI(SCRATCH1, gpr.R(IRREG_VFPU_CC), inst.dest); + regs_.MapGPR(IRREG_VFPU_CC, MIPSMap::DIRTY); + ANDI(SCRATCH1, regs_.R(IRREG_VFPU_CC), inst.dest); // This is the "any bit", easy. SNEZ(SCRATCH2, SCRATCH1); // To compare to inst.dest for "all", let's simply subtract it and compare to zero. @@ -541,8 +541,8 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { OR(SCRATCH1, SCRATCH1, SCRATCH2); // Reject those any/all bits and replace them with our own. - ANDI(gpr.R(IRREG_VFPU_CC), gpr.R(IRREG_VFPU_CC), ~0x30); - OR(gpr.R(IRREG_VFPU_CC), gpr.R(IRREG_VFPU_CC), SCRATCH1); + ANDI(regs_.R(IRREG_VFPU_CC), regs_.R(IRREG_VFPU_CC), ~0x30); + OR(regs_.R(IRREG_VFPU_CC), regs_.R(IRREG_VFPU_CC), SCRATCH1); break; default: @@ -581,21 +581,20 @@ void RiscVJitBackend::CompIR_FSpecial(IRInst inst) { #endif auto callFuncF_F = [&](float (*func)(float)){ - gpr.FlushBeforeCall(); - fpr.FlushBeforeCall(); + regs_.FlushBeforeCall(); // It might be in a non-volatile register. - if (fpr.IsFPRMapped(inst.src1)) { - FMV(32, F10, fpr.R(inst.src1)); + if (regs_.IsFPRMapped(inst.src1)) { + FMV(32, F10, regs_.F(inst.src1)); } else { int offset = offsetof(MIPSState, f) + inst.src1 * 4; FL(32, F10, CTXREG, offset); } QuickCallFunction(func, SCRATCH1); - fpr.MapFPR(inst.dest, MIPSMap::NOINIT); + regs_.MapFPR(inst.dest, MIPSMap::NOINIT); // If it's already F10, we're done - MapReg doesn't actually overwrite the reg in that case. - if (fpr.R(inst.dest) != F10) { - FMV(32, fpr.R(inst.dest), F10); + if (regs_.F(inst.dest) != F10) { + FMV(32, regs_.F(inst.dest), F10); } }; @@ -610,27 +609,27 @@ void RiscVJitBackend::CompIR_FSpecial(IRInst inst) { break; case IROp::FRSqrt: - tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1); - FSQRT(32, fpr.R(inst.dest), fpr.R(inst.src1)); + tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1); + FSQRT(32, regs_.F(inst.dest), regs_.F(inst.src1)); // Ugh, we can't really avoid a temp here. Probably not worth a permanent one. LI(SCRATCH1, 1.0f); FMV(FMv::W, FMv::X, tempReg, SCRATCH1); - FDIV(32, fpr.R(inst.dest), tempReg, fpr.R(inst.dest)); + FDIV(32, regs_.F(inst.dest), tempReg, regs_.F(inst.dest)); break; case IROp::FRecip: if (inst.dest != inst.src1) { // This is the easy case. - fpr.Map(inst); + regs_.Map(inst); LI(SCRATCH1, 1.0f); - FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); - FDIV(32, fpr.R(inst.dest), fpr.R(inst.dest), fpr.R(inst.src1)); + FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1); + FDIV(32, regs_.F(inst.dest), regs_.F(inst.dest), regs_.F(inst.src1)); } else { - tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1); + tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1); LI(SCRATCH1, 1.0f); FMV(FMv::W, FMv::X, tempReg, SCRATCH1); - FDIV(32, fpr.R(inst.dest), tempReg, fpr.R(inst.src1)); + FDIV(32, regs_.F(inst.dest), tempReg, regs_.F(inst.src1)); } break; diff --git a/Core/MIPS/RiscV/RiscVCompLoadStore.cpp b/Core/MIPS/RiscV/RiscVCompLoadStore.cpp index 223cb9abe1..e21c43ab9f 100644 --- a/Core/MIPS/RiscV/RiscVCompLoadStore.cpp +++ b/Core/MIPS/RiscV/RiscVCompLoadStore.cpp @@ -35,18 +35,18 @@ using namespace RiscVGen; using namespace RiscVJitConstants; void RiscVJitBackend::SetScratch1ToSrc1Address(IRReg src1) { - gpr.MapGPR(src1); + regs_.MapGPR(src1); #ifdef MASKED_PSP_MEMORY - SLLIW(SCRATCH1, gpr.R(src1), 2); + SLLIW(SCRATCH1, regs_.R(src1), 2); SRLIW(SCRATCH1, SCRATCH1, 2); ADD(SCRATCH1, SCRATCH1, MEMBASEREG); #else // Clear the top bits to be safe. if (cpu_info.RiscV_Zba) { - ADD_UW(SCRATCH1, gpr.R(src1), MEMBASEREG); + ADD_UW(SCRATCH1, regs_.R(src1), MEMBASEREG); } else { _assert_(XLEN == 64); - SLLI(SCRATCH1, gpr.R(src1), 32); + SLLI(SCRATCH1, regs_.R(src1), 32); SRLI(SCRATCH1, SCRATCH1, 32); ADD(SCRATCH1, SCRATCH1, MEMBASEREG); } @@ -66,7 +66,7 @@ int32_t RiscVJitBackend::AdjustForAddressOffset(RiscVGen::RiscVReg *reg, int32_t void RiscVJitBackend::CompIR_Load(IRInst inst) { CONDITIONAL_DISABLE; - gpr.SpillLockGPR(inst.dest, inst.src1); + regs_.SpillLockGPR(inst.dest, inst.src1); RiscVReg addrReg = INVALID_REG; if (inst.src1 == MIPS_REG_ZERO) { // This will get changed by AdjustForAddressOffset. @@ -74,14 +74,14 @@ void RiscVJitBackend::CompIR_Load(IRInst inst) { #ifdef MASKED_PSP_MEMORY inst.constant &= Memory::MEMVIEW32_MASK; #endif - } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { - addrReg = gpr.MapGPRAsPointer(inst.src1); + } else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) { + addrReg = regs_.MapGPRAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); addrReg = SCRATCH1; } // With NOINIT, MapReg won't subtract MEMBASEREG even if dest == src1. - gpr.MapGPR(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + regs_.MapGPR(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); @@ -89,29 +89,29 @@ void RiscVJitBackend::CompIR_Load(IRInst inst) { switch (inst.op) { case IROp::Load8: - LBU(gpr.R(inst.dest), addrReg, imm); + LBU(regs_.R(inst.dest), addrReg, imm); break; case IROp::Load8Ext: - LB(gpr.R(inst.dest), addrReg, imm); + LB(regs_.R(inst.dest), addrReg, imm); break; case IROp::Load16: - LHU(gpr.R(inst.dest), addrReg, imm); + LHU(regs_.R(inst.dest), addrReg, imm); break; case IROp::Load16Ext: - LH(gpr.R(inst.dest), addrReg, imm); + LH(regs_.R(inst.dest), addrReg, imm); break; case IROp::Load32: - LW(gpr.R(inst.dest), addrReg, imm); + LW(regs_.R(inst.dest), addrReg, imm); break; case IROp::Load32Linked: if (inst.dest != MIPS_REG_ZERO) - LW(gpr.R(inst.dest), addrReg, imm); - gpr.SetGPRImm(IRREG_LLBIT, 1); + LW(regs_.R(inst.dest), addrReg, imm); + regs_.SetGPRImm(IRREG_LLBIT, 1); break; default: @@ -146,8 +146,8 @@ void RiscVJitBackend::CompIR_FLoad(IRInst inst) { #ifdef MASKED_PSP_MEMORY inst.constant &= Memory::MEMVIEW32_MASK; #endif - } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { - addrReg = gpr.MapGPRAsPointer(inst.src1); + } else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) { + addrReg = regs_.MapGPRAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); addrReg = SCRATCH1; @@ -159,8 +159,8 @@ void RiscVJitBackend::CompIR_FLoad(IRInst inst) { switch (inst.op) { case IROp::LoadFloat: - fpr.MapFPR(inst.dest, MIPSMap::NOINIT); - FL(32, fpr.R(inst.dest), addrReg, imm); + regs_.MapFPR(inst.dest, MIPSMap::NOINIT); + FL(32, regs_.F(inst.dest), addrReg, imm); break; default: @@ -179,8 +179,8 @@ void RiscVJitBackend::CompIR_VecLoad(IRInst inst) { #ifdef MASKED_PSP_MEMORY inst.constant &= Memory::MEMVIEW32_MASK; #endif - } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { - addrReg = gpr.MapGPRAsPointer(inst.src1); + } else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) { + addrReg = regs_.MapGPRAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); addrReg = SCRATCH1; @@ -195,8 +195,8 @@ void RiscVJitBackend::CompIR_VecLoad(IRInst inst) { case IROp::LoadVec4: for (int i = 0; i < 4; ++i) { // Spilling is okay. - fpr.MapFPR(inst.dest + i, MIPSMap::NOINIT); - FL(32, fpr.R(inst.dest + i), addrReg, imm + 4 * i); + regs_.MapFPR(inst.dest + i, MIPSMap::NOINIT); + FL(32, regs_.F(inst.dest + i), addrReg, imm + 4 * i); } break; @@ -209,7 +209,7 @@ void RiscVJitBackend::CompIR_VecLoad(IRInst inst) { void RiscVJitBackend::CompIR_Store(IRInst inst) { CONDITIONAL_DISABLE; - gpr.SpillLockGPR(inst.src3, inst.src1); + regs_.SpillLockGPR(inst.src3, inst.src1); RiscVReg addrReg = INVALID_REG; if (inst.src1 == MIPS_REG_ZERO) { // This will get changed by AdjustForAddressOffset. @@ -217,15 +217,15 @@ void RiscVJitBackend::CompIR_Store(IRInst inst) { #ifdef MASKED_PSP_MEMORY inst.constant &= Memory::MEMVIEW32_MASK; #endif - } else if ((jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) { - addrReg = gpr.MapGPRAsPointer(inst.src1); + } else if ((jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) { + addrReg = regs_.MapGPRAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); addrReg = SCRATCH1; } - RiscVReg valueReg = gpr.TryMapTempImm(inst.src3); + RiscVReg valueReg = regs_.TryMapTempImm(inst.src3); if (valueReg == INVALID_REG) - valueReg = gpr.MapGPR(inst.src3); + valueReg = regs_.MapGPR(inst.src3); s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); @@ -255,7 +255,7 @@ void RiscVJitBackend::CompIR_CondStore(IRInst inst) { if (inst.op != IROp::Store32Conditional) INVALIDOP; - gpr.SpillLockGPR(IRREG_LLBIT, inst.src3, inst.src1); + regs_.SpillLockGPR(IRREG_LLBIT, inst.src3, inst.src1); RiscVReg addrReg = INVALID_REG; if (inst.src1 == MIPS_REG_ZERO) { // This will get changed by AdjustForAddressOffset. @@ -263,28 +263,28 @@ void RiscVJitBackend::CompIR_CondStore(IRInst inst) { #ifdef MASKED_PSP_MEMORY inst.constant &= Memory::MEMVIEW32_MASK; #endif - } else if ((jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) { - addrReg = gpr.MapGPRAsPointer(inst.src1); + } else if ((jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) { + addrReg = regs_.MapGPRAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); addrReg = SCRATCH1; } - gpr.MapGPR(inst.src3, inst.dest == MIPS_REG_ZERO ? MIPSMap::INIT : MIPSMap::DIRTY); - gpr.MapGPR(IRREG_LLBIT); + regs_.MapGPR(inst.src3, inst.dest == MIPS_REG_ZERO ? MIPSMap::INIT : MIPSMap::DIRTY); + regs_.MapGPR(IRREG_LLBIT); s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); // TODO: Safe memory? Or enough to have crash handler + validate? - FixupBranch condFailed = BEQ(gpr.R(IRREG_LLBIT), R_ZERO); - SW(gpr.R(inst.src3), addrReg, imm); + FixupBranch condFailed = BEQ(regs_.R(IRREG_LLBIT), R_ZERO); + SW(regs_.R(inst.src3), addrReg, imm); if (inst.dest != MIPS_REG_ZERO) { - LI(gpr.R(inst.dest), 1); + LI(regs_.R(inst.dest), 1); FixupBranch finish = J(); SetJumpTarget(condFailed); - LI(gpr.R(inst.dest), 0); + LI(regs_.R(inst.dest), 0); SetJumpTarget(finish); } else { SetJumpTarget(condFailed); @@ -317,8 +317,8 @@ void RiscVJitBackend::CompIR_FStore(IRInst inst) { #ifdef MASKED_PSP_MEMORY inst.constant &= Memory::MEMVIEW32_MASK; #endif - } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { - addrReg = gpr.MapGPRAsPointer(inst.src1); + } else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) { + addrReg = regs_.MapGPRAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); addrReg = SCRATCH1; @@ -330,8 +330,8 @@ void RiscVJitBackend::CompIR_FStore(IRInst inst) { switch (inst.op) { case IROp::StoreFloat: - fpr.MapFPR(inst.src3); - FS(32, fpr.R(inst.src3), addrReg, imm); + regs_.MapFPR(inst.src3); + FS(32, regs_.F(inst.src3), addrReg, imm); break; default: @@ -350,8 +350,8 @@ void RiscVJitBackend::CompIR_VecStore(IRInst inst) { #ifdef MASKED_PSP_MEMORY inst.constant &= Memory::MEMVIEW32_MASK; #endif - } else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) { - addrReg = gpr.MapGPRAsPointer(inst.src1); + } else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) { + addrReg = regs_.MapGPRAsPointer(inst.src1); } else { SetScratch1ToSrc1Address(inst.src1); addrReg = SCRATCH1; @@ -366,8 +366,8 @@ void RiscVJitBackend::CompIR_VecStore(IRInst inst) { case IROp::StoreVec4: for (int i = 0; i < 4; ++i) { // Spilling is okay, though not ideal. - fpr.MapFPR(inst.src3 + i); - FS(32, fpr.R(inst.src3 + i), addrReg, imm + 4 * i); + regs_.MapFPR(inst.src3 + i); + FS(32, regs_.F(inst.src3 + i), addrReg, imm + 4 * i); } break; diff --git a/Core/MIPS/RiscV/RiscVCompSystem.cpp b/Core/MIPS/RiscV/RiscVCompSystem.cpp index b655bbda74..c7dbb531db 100644 --- a/Core/MIPS/RiscV/RiscVCompSystem.cpp +++ b/Core/MIPS/RiscV/RiscVCompSystem.cpp @@ -45,18 +45,18 @@ void RiscVJitBackend::CompIR_Basic(IRInst inst) { case IROp::SetConst: // Sign extend all constants. We get 0xFFFFFFFF sometimes, and it's more work to truncate. // The register only holds 32 bits in the end anyway. - gpr.SetGPRImm(inst.dest, (int32_t)inst.constant); + regs_.SetGPRImm(inst.dest, (int32_t)inst.constant); break; case IROp::SetConstF: - fpr.Map(inst); + regs_.Map(inst); if (inst.constant == 0) { - FCVT(FConv::S, FConv::W, fpr.R(inst.dest), R_ZERO); + FCVT(FConv::S, FConv::W, regs_.F(inst.dest), R_ZERO); } else { // TODO: In the future, could use FLI if it's approved. // Also, is FCVT faster? LI(SCRATCH1, (int32_t)inst.constant); - FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1); } break; @@ -70,8 +70,8 @@ void RiscVJitBackend::CompIR_Basic(IRInst inst) { break; case IROp::SetPC: - gpr.Map(inst); - MovToPC(gpr.R(inst.src1)); + regs_.Map(inst); + MovToPC(regs_.R(inst.src1)); break; case IROp::SetPCConst: @@ -90,48 +90,48 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { switch (inst.op) { case IROp::SetCtrlVFPU: - gpr.SetGPRImm(IRREG_VFPU_CTRL_BASE + inst.dest, (int32_t)inst.constant); + regs_.SetGPRImm(IRREG_VFPU_CTRL_BASE + inst.dest, (int32_t)inst.constant); break; case IROp::SetCtrlVFPUReg: - gpr.MapDirtyIn(IRREG_VFPU_CTRL_BASE + inst.dest, inst.src1); - MV(gpr.R(IRREG_VFPU_CTRL_BASE + inst.dest), gpr.R(inst.src1)); - gpr.MarkGPRDirty(IRREG_VFPU_CTRL_BASE + inst.dest, gpr.IsNormalized32(inst.src1)); + regs_.MapGPRDirtyIn(IRREG_VFPU_CTRL_BASE + inst.dest, inst.src1); + MV(regs_.R(IRREG_VFPU_CTRL_BASE + inst.dest), regs_.R(inst.src1)); + regs_.MarkGPRDirty(IRREG_VFPU_CTRL_BASE + inst.dest, regs_.IsNormalized32(inst.src1)); break; case IROp::SetCtrlVFPUFReg: - gpr.MapGPR(IRREG_VFPU_CTRL_BASE + inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - fpr.MapFPR(inst.src1); - FMV(FMv::X, FMv::W, gpr.R(IRREG_VFPU_CTRL_BASE + inst.dest), fpr.R(inst.src1)); + regs_.MapGPR(IRREG_VFPU_CTRL_BASE + inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + regs_.MapFPR(inst.src1); + FMV(FMv::X, FMv::W, regs_.R(IRREG_VFPU_CTRL_BASE + inst.dest), regs_.F(inst.src1)); break; case IROp::FpCondFromReg: - gpr.MapDirtyIn(IRREG_FPCOND, inst.src1); - MV(gpr.R(IRREG_FPCOND), gpr.R(inst.src1)); + regs_.MapGPRDirtyIn(IRREG_FPCOND, inst.src1); + MV(regs_.R(IRREG_FPCOND), regs_.R(inst.src1)); break; case IROp::FpCondToReg: - gpr.MapDirtyIn(inst.dest, IRREG_FPCOND); - MV(gpr.R(inst.dest), gpr.R(IRREG_FPCOND)); - gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(IRREG_FPCOND)); + regs_.MapGPRDirtyIn(inst.dest, IRREG_FPCOND); + MV(regs_.R(inst.dest), regs_.R(IRREG_FPCOND)); + regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_FPCOND)); break; case IROp::FpCtrlFromReg: - gpr.MapDirtyIn(IRREG_FPCOND, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + regs_.MapGPRDirtyIn(IRREG_FPCOND, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); LI(SCRATCH1, 0x0181FFFF); - AND(SCRATCH1, gpr.R(inst.src1), SCRATCH1); + AND(SCRATCH1, regs_.R(inst.src1), SCRATCH1); // Extract the new fpcond value. if (cpu_info.RiscV_Zbs) { - BEXTI(gpr.R(IRREG_FPCOND), SCRATCH1, 23); + BEXTI(regs_.R(IRREG_FPCOND), SCRATCH1, 23); } else { - SRLI(gpr.R(IRREG_FPCOND), SCRATCH1, 23); - ANDI(gpr.R(IRREG_FPCOND), gpr.R(IRREG_FPCOND), 1); + SRLI(regs_.R(IRREG_FPCOND), SCRATCH1, 23); + ANDI(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND), 1); } SW(SCRATCH1, CTXREG, IRREG_FCR31 * 4); break; case IROp::FpCtrlToReg: - gpr.MapDirtyIn(inst.dest, IRREG_FPCOND, MapType::AVOID_LOAD_MARK_NORM32); + regs_.MapGPRDirtyIn(inst.dest, IRREG_FPCOND, MapType::AVOID_LOAD_MARK_NORM32); // Load fcr31 and clear the fpcond bit. LW(SCRATCH1, CTXREG, IRREG_FCR31 * 4); if (cpu_info.RiscV_Zbs) { @@ -142,34 +142,34 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { } // Now get the correct fpcond bit. - ANDI(SCRATCH2, gpr.R(IRREG_FPCOND), 1); + ANDI(SCRATCH2, regs_.R(IRREG_FPCOND), 1); SLLI(SCRATCH2, SCRATCH2, 23); - OR(gpr.R(inst.dest), SCRATCH1, SCRATCH2); + OR(regs_.R(inst.dest), SCRATCH1, SCRATCH2); // Also update mips->fcr31 while we're here. - SW(gpr.R(inst.dest), CTXREG, IRREG_FCR31 * 4); + SW(regs_.R(inst.dest), CTXREG, IRREG_FCR31 * 4); break; case IROp::VfpuCtrlToReg: - gpr.MapDirtyIn(inst.dest, IRREG_VFPU_CTRL_BASE + inst.src1); - MV(gpr.R(inst.dest), gpr.R(IRREG_VFPU_CTRL_BASE + inst.src1)); - gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(IRREG_VFPU_CTRL_BASE + inst.src1)); + regs_.MapGPRDirtyIn(inst.dest, IRREG_VFPU_CTRL_BASE + inst.src1); + MV(regs_.R(inst.dest), regs_.R(IRREG_VFPU_CTRL_BASE + inst.src1)); + regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_VFPU_CTRL_BASE + inst.src1)); break; case IROp::FMovFromGPR: - fpr.MapFPR(inst.dest, MIPSMap::NOINIT); - if (gpr.IsGPRImm(inst.src1) && gpr.GetGPRImm(inst.src1) == 0) { - FCVT(FConv::S, FConv::W, fpr.R(inst.dest), R_ZERO); + regs_.MapFPR(inst.dest, MIPSMap::NOINIT); + if (regs_.IsGPRImm(inst.src1) && regs_.GetGPRImm(inst.src1) == 0) { + FCVT(FConv::S, FConv::W, regs_.F(inst.dest), R_ZERO); } else { - gpr.MapGPR(inst.src1); - FMV(FMv::W, FMv::X, fpr.R(inst.dest), gpr.R(inst.src1)); + regs_.MapGPR(inst.src1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest), regs_.R(inst.src1)); } break; case IROp::FMovToGPR: - gpr.MapGPR(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - fpr.MapFPR(inst.src1); - FMV(FMv::X, FMv::W, gpr.R(inst.dest), fpr.R(inst.src1)); + regs_.MapGPR(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + regs_.MapFPR(inst.src1); + FMV(FMv::X, FMv::W, regs_.R(inst.dest), regs_.F(inst.src1)); break; default: diff --git a/Core/MIPS/RiscV/RiscVCompVec.cpp b/Core/MIPS/RiscV/RiscVCompVec.cpp index c196c3f68b..7c85b26162 100644 --- a/Core/MIPS/RiscV/RiscVCompVec.cpp +++ b/Core/MIPS/RiscV/RiscVCompVec.cpp @@ -40,36 +40,36 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) { switch (inst.op) { case IROp::Vec4Init: - fpr.Map(inst); + regs_.Map(inst); // TODO: Check if FCVT/FMV/FL is better. switch ((Vec4Init)inst.src1) { case Vec4Init::AllZERO: for (int i = 0; i < 4; ++i) - FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO); + FCVT(FConv::S, FConv::W, regs_.F(inst.dest + i), R_ZERO); break; case Vec4Init::AllONE: LI(SCRATCH1, 1.0f); - FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1); for (int i = 1; i < 4; ++i) - FMV(32, fpr.R(inst.dest + i), fpr.R(inst.dest)); + FMV(32, regs_.F(inst.dest + i), regs_.F(inst.dest)); break; case Vec4Init::AllMinusONE: LI(SCRATCH1, -1.0f); - FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1); for (int i = 1; i < 4; ++i) - FMV(32, fpr.R(inst.dest + i), fpr.R(inst.dest)); + FMV(32, regs_.F(inst.dest + i), regs_.F(inst.dest)); break; case Vec4Init::Set_1000: LI(SCRATCH1, 1.0f); for (int i = 0; i < 4; ++i) { if (i == 0) - FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest + i), SCRATCH1); else - FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO); + FCVT(FConv::S, FConv::W, regs_.F(inst.dest + i), R_ZERO); } break; @@ -77,9 +77,9 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) { LI(SCRATCH1, 1.0f); for (int i = 0; i < 4; ++i) { if (i == 1) - FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest + i), SCRATCH1); else - FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO); + FCVT(FConv::S, FConv::W, regs_.F(inst.dest + i), R_ZERO); } break; @@ -87,9 +87,9 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) { LI(SCRATCH1, 1.0f); for (int i = 0; i < 4; ++i) { if (i == 2) - FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest + i), SCRATCH1); else - FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO); + FCVT(FConv::S, FConv::W, regs_.F(inst.dest + i), R_ZERO); } break; @@ -97,9 +97,9 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) { LI(SCRATCH1, 1.0f); for (int i = 0; i < 4; ++i) { if (i == 3) - FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest + i), SCRATCH1); else - FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO); + FCVT(FConv::S, FConv::W, regs_.F(inst.dest + i), R_ZERO); } break; } @@ -107,7 +107,7 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) { case IROp::Vec4Shuffle: if (inst.dest == inst.src1) { - RiscVReg tempReg = fpr.Map4DirtyInTemp(inst.dest, inst.src1); + RiscVReg tempReg = regs_.MapFPR4DirtyInTemp(inst.dest, inst.src1); // Try to find the least swaps needed to move in place, never worse than 6 FMVs. // Would be better with a vmerge and vector regs. @@ -121,13 +121,13 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) { auto moveChained = [&](const std::vector &lanes, bool rotate) { int firstState = state[lanes.front()]; if (rotate) - FMV(32, tempReg, fpr.R(inst.dest + lanes.front())); + FMV(32, tempReg, regs_.F(inst.dest + lanes.front())); for (size_t i = 1; i < lanes.size(); ++i) { - FMV(32, fpr.R(inst.dest + lanes[i - 1]), fpr.R(inst.dest + lanes[i])); + FMV(32, regs_.F(inst.dest + lanes[i - 1]), regs_.F(inst.dest + lanes[i])); state[lanes[i - 1]] = state[lanes[i]]; } if (rotate) { - FMV(32, fpr.R(inst.dest + lanes.back()), tempReg); + FMV(32, regs_.F(inst.dest + lanes.back()), tempReg); state[lanes.back()] = firstState; } }; @@ -158,26 +158,26 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) { moveChained({ neededByDepth2, neededBy, i, foundIn }, neededByDepth3 == foundIn); } } else { - fpr.Map(inst); + regs_.Map(inst); for (int i = 0; i < 4; ++i) { int lane = (inst.src2 >> (i * 2)) & 3; - FMV(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + lane)); + FMV(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + lane)); } } break; case IROp::Vec4Blend: - fpr.Map(inst); + regs_.Map(inst); for (int i = 0; i < 4; ++i) { int which = (inst.constant >> i) & 1; - FMV(32, fpr.R(inst.dest + i), fpr.R((which ? inst.src2 : inst.src1) + i)); + FMV(32, regs_.F(inst.dest + i), regs_.F((which ? inst.src2 : inst.src1) + i)); } break; case IROp::Vec4Mov: - fpr.Map(inst); + regs_.Map(inst); for (int i = 0; i < 4; ++i) - FMV(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i)); + FMV(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i)); break; default: @@ -191,47 +191,47 @@ void RiscVJitBackend::CompIR_VecArith(IRInst inst) { switch (inst.op) { case IROp::Vec4Add: - fpr.Map(inst); + regs_.Map(inst); for (int i = 0; i < 4; ++i) - FADD(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i)); + FADD(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i)); break; case IROp::Vec4Sub: - fpr.Map(inst); + regs_.Map(inst); for (int i = 0; i < 4; ++i) - FSUB(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i)); + FSUB(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i)); break; case IROp::Vec4Mul: - fpr.Map(inst); + regs_.Map(inst); for (int i = 0; i < 4; ++i) - FMUL(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i)); + FMUL(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i)); break; case IROp::Vec4Div: - fpr.Map(inst); + regs_.Map(inst); for (int i = 0; i < 4; ++i) - FDIV(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i)); + FDIV(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i)); break; case IROp::Vec4Scale: - fpr.SpillLockFPR(inst.src2); - fpr.MapFPR(inst.src2); - fpr.Map4DirtyIn(inst.dest, inst.src1); + regs_.SpillLockFPR(inst.src2); + regs_.MapFPR(inst.src2); + regs_.MapFPR4DirtyIn(inst.dest, inst.src1); for (int i = 0; i < 4; ++i) - FMUL(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2)); + FMUL(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i), regs_.F(inst.src2)); break; case IROp::Vec4Neg: - fpr.Map(inst); + regs_.Map(inst); for (int i = 0; i < 4; ++i) - FNEG(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i)); + FNEG(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i)); break; case IROp::Vec4Abs: - fpr.Map(inst); + regs_.Map(inst); for (int i = 0; i < 4; ++i) - FABS(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i)); + FABS(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i)); break; default: @@ -246,32 +246,32 @@ void RiscVJitBackend::CompIR_VecHoriz(IRInst inst) { switch (inst.op) { case IROp::Vec4Dot: // TODO: Maybe some option to call the slow accurate mode? - fpr.SpillLockFPR(inst.dest); + regs_.SpillLockFPR(inst.dest); for (int i = 0; i < 4; ++i) { - fpr.SpillLockFPR(inst.src1 + i); - fpr.SpillLockFPR(inst.src2 + i); + regs_.SpillLockFPR(inst.src1 + i); + regs_.SpillLockFPR(inst.src2 + i); } for (int i = 0; i < 4; ++i) { - fpr.MapFPR(inst.src1 + i); - fpr.MapFPR(inst.src2 + i); + regs_.MapFPR(inst.src1 + i); + regs_.MapFPR(inst.src2 + i); } - fpr.MapFPR(inst.dest, MIPSMap::NOINIT); + regs_.MapFPR(inst.dest, MIPSMap::NOINIT); if ((inst.dest < inst.src1 + 4 && inst.dest >= inst.src1) || (inst.dest < inst.src2 + 4 && inst.dest >= inst.src2)) { // This means inst.dest overlaps one of src1 or src2. We have to do that one first. // Technically this may impact -0.0 and such, but dots accurately need to be aligned anyway. for (int i = 0; i < 4; ++i) { if (inst.dest == inst.src1 + i || inst.dest == inst.src2 + i) - FMUL(32, fpr.R(inst.dest), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i)); + FMUL(32, regs_.F(inst.dest), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i)); } for (int i = 0; i < 4; ++i) { if (inst.dest != inst.src1 + i && inst.dest != inst.src2 + i) - FMADD(32, fpr.R(inst.dest), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i), fpr.R(inst.dest)); + FMADD(32, regs_.F(inst.dest), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i), regs_.F(inst.dest)); } } else { - FMUL(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2)); + FMUL(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2)); for (int i = 1; i < 4; ++i) - FMADD(32, fpr.R(inst.dest), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i), fpr.R(inst.dest)); + FMADD(32, regs_.F(inst.dest), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i), regs_.F(inst.dest)); } break; @@ -292,14 +292,14 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { break; case IROp::Vec4Unpack8To32: - fpr.SpillLockFPR(inst.src1); + regs_.SpillLockFPR(inst.src1); for (int i = 0; i < 4; ++i) - fpr.SpillLockFPR(inst.dest + i); - fpr.MapFPR(inst.src1); + regs_.SpillLockFPR(inst.dest + i); + regs_.MapFPR(inst.src1); for (int i = 0; i < 4; ++i) - fpr.MapFPR(inst.dest + i, MIPSMap::NOINIT); + regs_.MapFPR(inst.dest + i, MIPSMap::NOINIT); - FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1)); + FMV(FMv::X, FMv::W, SCRATCH2, regs_.F(inst.src1)); for (int i = 0; i < 4; ++i) { // Mask using walls. if (i != 0) { @@ -308,49 +308,49 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { } else { SLLI(SCRATCH1, SCRATCH2, 24); } - FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest + i), SCRATCH1); } break; case IROp::Vec2Unpack16To32: - fpr.SpillLockFPR(inst.src1); + regs_.SpillLockFPR(inst.src1); for (int i = 0; i < 2; ++i) - fpr.SpillLockFPR(inst.dest + i); - fpr.MapFPR(inst.src1); + regs_.SpillLockFPR(inst.dest + i); + regs_.MapFPR(inst.src1); for (int i = 0; i < 2; ++i) - fpr.MapFPR(inst.dest + i, MIPSMap::NOINIT); + regs_.MapFPR(inst.dest + i, MIPSMap::NOINIT); - FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1)); + FMV(FMv::X, FMv::W, SCRATCH2, regs_.F(inst.src1)); SLLI(SCRATCH1, SCRATCH2, 16); - FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1); SRLI(SCRATCH1, SCRATCH2, 16); SLLI(SCRATCH1, SCRATCH1, 16); - FMV(FMv::W, FMv::X, fpr.R(inst.dest + 1), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest + 1), SCRATCH1); break; case IROp::Vec4DuplicateUpperBitsAndShift1: - fpr.Map(inst); + regs_.Map(inst); for (int i = 0; i < 4; i++) { - FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1 + i)); + FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1 + i)); SRLIW(SCRATCH2, SCRATCH1, 8); OR(SCRATCH1, SCRATCH1, SCRATCH2); SRLIW(SCRATCH2, SCRATCH1, 16); OR(SCRATCH1, SCRATCH1, SCRATCH2); SRLIW(SCRATCH1, SCRATCH1, 1); - FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest + i), SCRATCH1); } break; case IROp::Vec4Pack31To8: - fpr.SpillLockFPR(inst.dest); + regs_.SpillLockFPR(inst.dest); for (int i = 0; i < 4; ++i) { - fpr.SpillLockFPR(inst.src1 + i); - fpr.MapFPR(inst.src1 + i); + regs_.SpillLockFPR(inst.src1 + i); + regs_.MapFPR(inst.src1 + i); } - fpr.MapFPR(inst.dest, MIPSMap::NOINIT); + regs_.MapFPR(inst.dest, MIPSMap::NOINIT); for (int i = 0; i < 4; ++i) { - FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1 + i)); + FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1 + i)); SRLI(SCRATCH1, SCRATCH1, 23); if (i == 0) { ANDI(SCRATCH2, SCRATCH1, 0xFF); @@ -361,13 +361,13 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { } } - FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH2); + FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH2); break; case IROp::Vec2Pack32To16: - fpr.MapDirtyInIn(inst.dest, inst.src1, inst.src1 + 1); - FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1)); - FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1 + 1)); + regs_.MapFPRDirtyInIn(inst.dest, inst.src1, inst.src1 + 1); + FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1)); + FMV(FMv::X, FMv::W, SCRATCH2, regs_.F(inst.src1 + 1)); // Keep in mind, this was sign-extended, so we have to zero the upper. SLLI(SCRATCH1, SCRATCH1, XLEN - 32); // Now we just set (SCRATCH2 & 0xFFFF0000) | SCRATCH1. @@ -377,7 +377,7 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { SLLI(SCRATCH2, SCRATCH2, 16); OR(SCRATCH1, SCRATCH1, SCRATCH2); // Okay, to the floating point register. - FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1); break; default: @@ -391,9 +391,9 @@ void RiscVJitBackend::CompIR_VecClamp(IRInst inst) { switch (inst.op) { case IROp::Vec4ClampToZero: - fpr.Map(inst); + regs_.Map(inst); for (int i = 0; i < 4; i++) { - FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1 + i)); + FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1 + i)); SRAIW(SCRATCH2, SCRATCH1, 31); if (cpu_info.RiscV_Zbb) { ANDN(SCRATCH1, SCRATCH1, SCRATCH2); @@ -401,7 +401,7 @@ void RiscVJitBackend::CompIR_VecClamp(IRInst inst) { NOT(SCRATCH2, SCRATCH2); AND(SCRATCH1, SCRATCH1, SCRATCH2); } - FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1); + FMV(FMv::W, FMv::X, regs_.F(inst.dest + i), SCRATCH1); } break; diff --git a/Core/MIPS/RiscV/RiscVJit.cpp b/Core/MIPS/RiscV/RiscVJit.cpp index b31e762d39..1e9e4ef04c 100644 --- a/Core/MIPS/RiscV/RiscVJit.cpp +++ b/Core/MIPS/RiscV/RiscVJit.cpp @@ -32,7 +32,7 @@ static constexpr int MIN_BLOCK_NORMAL_LEN = 16; static constexpr int MIN_BLOCK_EXIT_LEN = 8; RiscVJitBackend::RiscVJitBackend(JitOptions &jitopt, IRBlockCache &blocks) - : IRNativeBackend(blocks), jo(jitopt), gpr(&jo), fpr(&jo) { + : IRNativeBackend(blocks), jo(jitopt), regs_(&jo) { // Automatically disable incompatible options. if (((intptr_t)Memory::base & 0x00000000FFFFFFFFUL) != 0) { jo.enablePointerify = false; @@ -43,8 +43,7 @@ RiscVJitBackend::RiscVJitBackend(JitOptions &jitopt, IRBlockCache &blocks) AllocCodeSpace(1024 * 1024 * 16); SetAutoCompress(true); - gpr.Init(this); - fpr.Init(this); + regs_.Init(this); } RiscVJitBackend::~RiscVJitBackend() { @@ -76,23 +75,19 @@ bool RiscVJitBackend::CompileBlock(IRBlock *block, int block_num, bool preload) block->SetTargetOffset((int)GetOffset(blockStart)); compilingBlockNum_ = block_num; - gpr.Start(block); - fpr.Start(block); + regs_.Start(block); std::map addresses; for (int i = 0; i < block->GetNumInstructions(); ++i) { const IRInst &inst = block->GetInstructions()[i]; - gpr.SetIRIndex(i); - fpr.SetIRIndex(i); + regs_.SetIRIndex(i); // TODO: This might be a little wasteful when compiling if we're not debugging jit... addresses[GetCodePtr()] = inst; CompileIRInst(inst); - if (jo.Disabled(JitDisable::REGALLOC_GPR)) - gpr.FlushAll(); - if (jo.Disabled(JitDisable::REGALLOC_FPR)) - fpr.FlushAll(); + if (jo.Disabled(JitDisable::REGALLOC_GPR) || jo.Disabled(JitDisable::REGALLOC_FPR)) + regs_.FlushAll(jo.Disabled(JitDisable::REGALLOC_GPR), jo.Disabled(JitDisable::REGALLOC_FPR)); // Safety check, in case we get a bunch of really large jit ops without a lot of branching. if (GetSpaceLeft() < 0x800) { @@ -252,8 +247,7 @@ void RiscVJitBackend::CompIR_Interpret(IRInst inst) { } void RiscVJitBackend::FlushAll() { - gpr.FlushAll(); - fpr.FlushAll(); + regs_.FlushAll(); } bool RiscVJitBackend::DescribeCodePtr(const u8 *ptr, std::string &name) const { @@ -355,12 +349,12 @@ void RiscVJitBackend::NormalizeSrc12(IRInst inst, RiscVReg *lhs, RiscVReg *rhs, RiscVReg RiscVJitBackend::NormalizeR(IRReg rs, IRReg rd, RiscVReg tempReg) { // For proper compare, we must sign extend so they both match or don't match. // But don't change pointers, in case one is SP (happens in LittleBigPlanet.) - if (gpr.IsGPRImm(rs) && gpr.GetGPRImm(rs) == 0) { + if (regs_.IsGPRImm(rs) && regs_.GetGPRImm(rs) == 0) { return R_ZERO; - } else if (gpr.IsGPRMappedAsPointer(rs) || rs == rd) { - return gpr.Normalize32(rs, tempReg); + } else if (regs_.IsGPRMappedAsPointer(rs) || rs == rd) { + return regs_.Normalize32(rs, tempReg); } else { - return gpr.Normalize32(rs); + return regs_.Normalize32(rs); } } diff --git a/Core/MIPS/RiscV/RiscVJit.h b/Core/MIPS/RiscV/RiscVJit.h index 228fce48ab..46448c4c71 100644 --- a/Core/MIPS/RiscV/RiscVJit.h +++ b/Core/MIPS/RiscV/RiscVJit.h @@ -25,7 +25,6 @@ #include "Core/MIPS/JitCommon/JitState.h" #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/MIPS/RiscV/RiscVRegCache.h" -#include "Core/MIPS/RiscV/RiscVRegCacheFPU.h" namespace MIPSComp { @@ -112,8 +111,7 @@ private: RiscVGen::RiscVReg NormalizeR(IRReg rs, IRReg rd, RiscVGen::RiscVReg tempReg); JitOptions &jo; - RiscVRegCache gpr; - RiscVRegCacheFPU fpr; + RiscVRegCache regs_; const u8 *outerLoop_ = nullptr; const u8 *outerLoopPCInSCRATCH1_ = nullptr; diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index e0351b0b4a..f172b6ef67 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -31,8 +31,10 @@ using namespace RiscVJitConstants; RiscVRegCache::RiscVRegCache(MIPSComp::JitOptions *jo) : IRNativeRegCacheBase(jo) { - // TODO: Move to using for FPRs and VPRs too? - config_.totalNativeRegs = NUM_RVREG; + // TODO: Update these when using RISC-V V. + config_.totalNativeRegs = NUM_RVGPR + NUM_RVFPR; + config_.mapUseVRegs = false; + config_.mapSIMD = false; } void RiscVRegCache::Init(RiscVEmitter *emitter) { @@ -54,23 +56,40 @@ void RiscVRegCache::SetupInitialRegs() { } const int *RiscVRegCache::GetAllocationOrder(MIPSLoc type, int &count, int &base) const { - _assert_(type == MIPSLoc::REG); - // X8 and X9 are the most ideal for static alloc because they can be used with compression. - // Otherwise we stick to saved regs - might not be necessary. - static const int allocationOrder[] = { - X8, X9, X12, X13, X14, X15, X5, X6, X7, X16, X17, X18, X19, X20, X21, X22, X23, X28, X29, X30, X31, - }; - static const int allocationOrderStaticAlloc[] = { - X12, X13, X14, X15, X5, X6, X7, X16, X17, X21, X22, X23, X28, X29, X30, X31, - }; - base = X0; - if (jo_->useStaticAlloc) { - count = ARRAY_SIZE(allocationOrderStaticAlloc); - return allocationOrderStaticAlloc; - } else { + + if (type == MIPSLoc::REG) { + // X8 and X9 are the most ideal for static alloc because they can be used with compression. + // Otherwise we stick to saved regs - might not be necessary. + static const int allocationOrder[] = { + X8, X9, X12, X13, X14, X15, X5, X6, X7, X16, X17, X18, X19, X20, X21, X22, X23, X28, X29, X30, X31, + }; + static const int allocationOrderStaticAlloc[] = { + X12, X13, X14, X15, X5, X6, X7, X16, X17, X21, X22, X23, X28, X29, X30, X31, + }; + + if (jo_->useStaticAlloc) { + count = ARRAY_SIZE(allocationOrderStaticAlloc); + return allocationOrderStaticAlloc; + } else { + count = ARRAY_SIZE(allocationOrder); + return allocationOrder; + } + } else if (type == MIPSLoc::FREG) { + // F8 through F15 are used for compression, so they are great. + // TODO: Maybe we could remove some saved regs since we rarely need that many? Or maybe worth it? + static const int allocationOrder[] = { + F8, F9, F10, F11, F12, F13, F14, F15, + F0, F1, F2, F3, F4, F5, F6, F7, + F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31, + }; + count = ARRAY_SIZE(allocationOrder); return allocationOrder; + } else { + _assert_msg_(false, "Allocation order not yet implemented"); + count = 0; + return nullptr; } } @@ -116,14 +135,18 @@ void RiscVRegCache::EmitSaveStaticRegisters() { void RiscVRegCache::FlushBeforeCall() { // These registers are not preserved by function calls. + // They match between X0 and F0, conveniently. for (int i = 5; i <= 7; ++i) { - FlushNativeReg(i); + FlushNativeReg(X0 + i); + FlushNativeReg(F0 + i); } for (int i = 10; i <= 17; ++i) { - FlushNativeReg(i); + FlushNativeReg(X0 + i); + FlushNativeReg(F0 + i); } for (int i = 28; i <= 31; ++i) { - FlushNativeReg(i); + FlushNativeReg(X0 + i); + FlushNativeReg(F0 + i); } } @@ -232,7 +255,7 @@ RiscVReg RiscVRegCache::MapGPRAsPointer(IRReg reg) { return (RiscVReg)MapNativeRegAsPointer(reg); } -void RiscVRegCache::MapDirtyIn(IRReg rd, IRReg rs, MapType type) { +void RiscVRegCache::MapGPRDirtyIn(IRReg rd, IRReg rs, MapType type) { SpillLockGPR(rd, rs); bool load = type == MapType::ALWAYS_LOAD || rd == rs; MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; @@ -241,7 +264,7 @@ void RiscVRegCache::MapDirtyIn(IRReg rd, IRReg rs, MapType type) { ReleaseSpillLockGPR(rd, rs); } -void RiscVRegCache::MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, MapType type) { +void RiscVRegCache::MapGPRDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, MapType type) { SpillLockGPR(rd1, rd2, rs, rt); bool load1 = type == MapType::ALWAYS_LOAD || (rd1 == rs || rd1 == rt); bool load2 = type == MapType::ALWAYS_LOAD || (rd2 == rs || rd2 == rt); @@ -253,6 +276,61 @@ void RiscVRegCache::MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, ReleaseSpillLockGPR(rd1, rd2, rs, rt); } +RiscVReg RiscVRegCache::MapFPR(IRReg mipsReg, MIPSMap mapFlags) { + _dbg_assert_(IsValidFPR(mipsReg)); + _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::MEM || mr[mipsReg + 32].loc == MIPSLoc::FREG); + + IRNativeReg nreg = MapNativeReg(MIPSLoc::FREG, mipsReg + 32, 1, mapFlags); + if (nreg != -1) + return (RiscVReg)nreg; + return INVALID_REG; +} + +void RiscVRegCache::MapFPRDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad) { + SpillLockFPR(rd, rs, rt); + bool load = !avoidLoad || (rd == rs || rd == rt); + MapFPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); + MapFPR(rt); + MapFPR(rs); + ReleaseSpillLockFPR(rd, rs, rt); +} + +RiscVReg RiscVRegCache::MapFPRDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad) { + SpillLockFPR(rd, rs); + bool load = !avoidLoad || rd == rs; + MapFPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); + MapFPR(rs); + RiscVReg temp = (RiscVReg)AllocateReg(MIPSLoc::FREG); + ReleaseSpillLockFPR(rd, rs); + return temp; +} + +void RiscVRegCache::MapFPR4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad) { + for (int i = 0; i < 4; ++i) + SpillLockFPR(rdbase + i, rsbase + i); + bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase); + for (int i = 0; i < 4; ++i) + MapFPR(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); + for (int i = 0; i < 4; ++i) + MapFPR(rsbase + i); + for (int i = 0; i < 4; ++i) + ReleaseSpillLockFPR(rdbase + i, rsbase + i); +} + +RiscVReg RiscVRegCache::MapFPR4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad) { + for (int i = 0; i < 4; ++i) + SpillLockFPR(rdbase + i, rsbase + i); + bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase); + for (int i = 0; i < 4; ++i) + MapFPR(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); + for (int i = 0; i < 4; ++i) + MapFPR(rsbase + i); + RiscVReg temp = (RiscVReg)AllocateReg(MIPSLoc::FREG); + for (int i = 0; i < 4; ++i) + ReleaseSpillLockFPR(rdbase + i, rsbase + i); + return temp; +} + void RiscVRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { RiscVReg r = (RiscVReg)(X0 + nreg); _assert_(r >= X0 && r <= X31); @@ -286,32 +364,54 @@ void RiscVRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { void RiscVRegCache::LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) { RiscVReg r = (RiscVReg)(X0 + nreg); - _dbg_assert_(r > X0 && r <= X31); + _dbg_assert_(r > X0); _dbg_assert_(first != MIPS_REG_ZERO); - // Multilane not yet supported. - _assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO)); - if (lanes == 1) - emit_->LW(r, CTXREG, GetMipsRegOffset(first)); - else if (lanes == 2) - emit_->LD(r, CTXREG, GetMipsRegOffset(first)); - else - _assert_(false); - nr[nreg].normalized32 = true; + if (r <= X31) { + // Multilane not yet supported. + _assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO)); + if (lanes == 1) + emit_->LW(r, CTXREG, GetMipsRegOffset(first)); + else if (lanes == 2) + emit_->LD(r, CTXREG, GetMipsRegOffset(first)); + else + _assert_(false); + nr[nreg].normalized32 = true; + } else { + _dbg_assert_(r >= F0 && r <= F31); + // Multilane not yet supported. + _assert_(lanes == 1); + if (mr[first].loc == MIPSLoc::FREG) { + emit_->FL(32, r, CTXREG, GetMipsRegOffset(first)); + } else { + _assert_msg_(mr[first].loc == MIPSLoc::FREG, "Cannot store this type: %d", (int)mr[first].loc); + } + } } void RiscVRegCache::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) { RiscVReg r = (RiscVReg)(X0 + nreg); - _dbg_assert_(r > X0 && r <= X31); + _dbg_assert_(r > X0); _dbg_assert_(first != MIPS_REG_ZERO); - // Multilane not yet supported. - _assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO)); - _assert_(mr[first].loc == MIPSLoc::REG || mr[first].loc == MIPSLoc::REG_IMM); - if (lanes == 1) - emit_->SW(r, CTXREG, GetMipsRegOffset(first)); - else if (lanes == 2) - emit_->SD(r, CTXREG, GetMipsRegOffset(first)); - else - _assert_(false); + if (r <= X31) { + // Multilane not yet supported. + _assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO)); + _assert_(mr[first].loc == MIPSLoc::REG || mr[first].loc == MIPSLoc::REG_IMM); + if (lanes == 1) + emit_->SW(r, CTXREG, GetMipsRegOffset(first)); + else if (lanes == 2) + emit_->SD(r, CTXREG, GetMipsRegOffset(first)); + else + _assert_(false); + } else { + _dbg_assert_(r >= F0 && r <= F31); + // Multilane not yet supported. + _assert_(lanes == 1); + if (mr[first].loc == MIPSLoc::FREG) { + emit_->FS(32, r, CTXREG, GetMipsRegOffset(first)); + } else { + _assert_msg_(mr[first].loc == MIPSLoc::FREG, "Cannot store this type: %d", (int)mr[first].loc); + } + } } void RiscVRegCache::SetNativeRegValue(IRNativeReg nreg, uint32_t imm) { @@ -326,7 +426,7 @@ void RiscVRegCache::SetNativeRegValue(IRNativeReg nreg, uint32_t imm) { } void RiscVRegCache::StoreRegValue(IRReg mreg, uint32_t imm) { - _assert_(mreg != MIPS_REG_ZERO); + _assert_(IsValidGPRNoZero(mreg)); // Try to optimize using a different reg. RiscVReg storeReg = INVALID_REG; @@ -382,3 +482,14 @@ RiscVReg RiscVRegCache::RPtr(IRReg mipsReg) { return INVALID_REG; // BAAAD } } + +RiscVReg RiscVRegCache::F(IRReg mipsReg) { + _dbg_assert_(IsValidFPR(mipsReg)); + _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::FREG); + if (mr[mipsReg + 32].loc == MIPSLoc::FREG) { + return (RiscVReg)mr[mipsReg + 32].nReg; + } else { + ERROR_LOG_REPORT(JIT, "Reg %i not in riscv reg", mipsReg); + return INVALID_REG; // BAAAD + } +} diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index ccf309207c..26cf0d9894 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -56,18 +56,29 @@ public: RiscVGen::RiscVReg MapGPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); RiscVGen::RiscVReg MapGPRAsPointer(IRReg reg); + void MapGPRDirtyIn(IRReg rd, IRReg rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); + void MapGPRDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); + + // Returns a RISC-V register containing the requested MIPS register. + RiscVGen::RiscVReg MapFPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); + + void MapFPRDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad = true); + RiscVGen::RiscVReg MapFPRDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad = true); + void MapFPR4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad = true); + RiscVGen::RiscVReg MapFPR4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad = true); + bool IsNormalized32(IRReg reg); // Copies to another reg if specified, otherwise same reg. RiscVGen::RiscVReg Normalize32(IRReg reg, RiscVGen::RiscVReg destReg = RiscVGen::INVALID_REG); - void MapDirtyIn(IRReg rd, IRReg rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); - void MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); + void FlushBeforeCall(); RiscVGen::RiscVReg GetAndLockTempR(); RiscVGen::RiscVReg R(IRReg preg); // Returns a cached register, while checking that it's NOT mapped as a pointer RiscVGen::RiscVReg RPtr(IRReg preg); // Returns a cached register, if it has been mapped as a pointer + RiscVGen::RiscVReg F(IRReg preg); // These are called once on startup to generate functions, that you should then call. void EmitLoadStaticRegisters(); @@ -88,6 +99,8 @@ private: RiscVGen::RiscVEmitter *emit_ = nullptr; enum { - NUM_RVREG = 32, + NUM_RVGPR = 32, + NUM_RVFPR = 32, + NUM_RVVPR = 32, }; }; diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp deleted file mode 100644 index 9b544f450c..0000000000 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) 2023- PPSSPP Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official git repository and contact information can be found at -// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. - -#ifndef offsetof -#include -#endif - -#include "Common/CPUDetect.h" -#include "Core/MIPS/IR/IRInst.h" -#include "Core/MIPS/IR/IRAnalysis.h" -#include "Core/MIPS/RiscV/RiscVRegCacheFPU.h" -#include "Core/MIPS/JitCommon/JitState.h" -#include "Core/Reporting.h" - -using namespace RiscVGen; -using namespace RiscVJitConstants; - -RiscVRegCacheFPU::RiscVRegCacheFPU(MIPSComp::JitOptions *jo) - : IRNativeRegCacheBase(jo) { - config_.totalNativeRegs = NUM_RVFPUREG; - // TODO: When using RISC-V V. - config_.mapUseVRegs = false; - config_.mapSIMD = false; -} - -void RiscVRegCacheFPU::Init(RiscVEmitter *emitter) { - emit_ = emitter; -} - -const int *RiscVRegCacheFPU::GetAllocationOrder(MIPSLoc type, int &count, int &base) const { - _assert_(type == MIPSLoc::FREG); - // F8 through F15 are used for compression, so they are great. - // TODO: Maybe we could remove some saved regs since we rarely need that many? Or maybe worth it? - static const int allocationOrder[] = { - F8, F9, F10, F11, F12, F13, F14, F15, - F0, F1, F2, F3, F4, F5, F6, F7, - F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31, - }; - - count = ARRAY_SIZE(allocationOrder); - base = F0; - return allocationOrder; -} - -RiscVReg RiscVRegCacheFPU::MapFPR(IRReg mipsReg, MIPSMap mapFlags) { - _dbg_assert_(IsValidFPR(mipsReg)); - _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::MEM || mr[mipsReg + 32].loc == MIPSLoc::FREG); - - IRNativeReg nreg = MapNativeReg(MIPSLoc::FREG, mipsReg + 32, 1, mapFlags); - if (nreg != -1) - return (RiscVReg)(F0 + nreg); - return INVALID_REG; -} - -void RiscVRegCacheFPU::MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad) { - SpillLockFPR(rd, rs, rt); - bool load = !avoidLoad || (rd == rs || rd == rt); - MapFPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); - MapFPR(rt); - MapFPR(rs); - ReleaseSpillLockFPR(rd, rs, rt); -} - -RiscVReg RiscVRegCacheFPU::MapDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad) { - SpillLockFPR(rd, rs); - bool load = !avoidLoad || rd == rs; - MapFPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); - MapFPR(rs); - RiscVReg temp = (RiscVReg)(F0 + AllocateReg(MIPSLoc::FREG)); - ReleaseSpillLockFPR(rd, rs); - return temp; -} - -void RiscVRegCacheFPU::Map4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad) { - for (int i = 0; i < 4; ++i) - SpillLockFPR(rdbase + i, rsbase + i); - bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase); - for (int i = 0; i < 4; ++i) - MapFPR(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); - for (int i = 0; i < 4; ++i) - MapFPR(rsbase + i); - for (int i = 0; i < 4; ++i) - ReleaseSpillLockFPR(rdbase + i, rsbase + i); -} - -RiscVReg RiscVRegCacheFPU::Map4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad) { - for (int i = 0; i < 4; ++i) - SpillLockFPR(rdbase + i, rsbase + i); - bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase); - for (int i = 0; i < 4; ++i) - MapFPR(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); - for (int i = 0; i < 4; ++i) - MapFPR(rsbase + i); - RiscVReg temp = (RiscVReg)(F0 + AllocateReg(MIPSLoc::FREG)); - for (int i = 0; i < 4; ++i) - ReleaseSpillLockFPR(rdbase + i, rsbase + i); - return temp; -} - -void RiscVRegCacheFPU::LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) { - RiscVReg r = (RiscVReg)(F0 + nreg); - _dbg_assert_(r >= F0 && r <= F31); - // Multilane not yet supported. - _assert_(lanes == 1); - if (mr[first].loc == MIPSLoc::FREG) { - emit_->FL(32, r, CTXREG, GetMipsRegOffset(first)); - } else { - _assert_msg_(mr[first].loc == MIPSLoc::FREG, "Cannot store this type: %d", (int)mr[first].loc); - } -} - -void RiscVRegCacheFPU::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) { - RiscVReg r = (RiscVReg)(F0 + nreg); - _dbg_assert_(r >= F0 && r <= F31); - // Multilane not yet supported. - _assert_(lanes == 1); - if (mr[first].loc == MIPSLoc::FREG) { - emit_->FS(32, r, CTXREG, GetMipsRegOffset(first)); - } else { - _assert_msg_(mr[first].loc == MIPSLoc::FREG, "Cannot store this type: %d", (int)mr[first].loc); - } -} - -void RiscVRegCacheFPU::SetNativeRegValue(IRNativeReg nreg, uint32_t imm) { - _assert_msg_(false, "Set float to imm is unsupported"); -} - -void RiscVRegCacheFPU::StoreRegValue(IRReg mreg, uint32_t imm) { - _assert_msg_(false, "Storing imms to floats is unsupported"); -} - -void RiscVRegCacheFPU::FlushBeforeCall() { - // These registers are not preserved by function calls. - for (int i = 0; i <= 7; ++i) { - FlushNativeReg(i); - } - for (int i = 10; i <= 17; ++i) { - FlushNativeReg(i); - } - for (int i = 28; i <= 31; ++i) { - FlushNativeReg(i); - } -} - -RiscVReg RiscVRegCacheFPU::R(IRReg mipsReg) { - _dbg_assert_(IsValidFPR(mipsReg)); - _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::FREG); - if (mr[mipsReg + 32].loc == MIPSLoc::FREG) { - return (RiscVReg)(mr[mipsReg + 32].nReg + F0); - } else { - ERROR_LOG_REPORT(JIT, "Reg %i not in riscv reg", mipsReg); - return INVALID_REG; // BAAAD - } -} - -void RiscVRegCacheFPU::SetupInitialRegs() { - IRNativeRegCacheBase::SetupInitialRegs(); - - // TODO: Move to a shared cache? - mrInitial_[0].loc = MIPSLoc::MEM; -} diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.h b/Core/MIPS/RiscV/RiscVRegCacheFPU.h deleted file mode 100644 index e651436b48..0000000000 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2023- PPSSPP Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official git repository and contact information can be found at -// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. - -#pragma once - -#include "Common/RiscVEmitter.h" -#include "Core/MIPS/MIPS.h" -#include "Core/MIPS/RiscV/RiscVRegCache.h" - -namespace MIPSComp { -struct JitOptions; -} - -class RiscVRegCacheFPU : public IRNativeRegCacheBase { -public: - RiscVRegCacheFPU(MIPSComp::JitOptions *jo); - - void Init(RiscVGen::RiscVEmitter *emitter); - - // Returns a RISC-V register containing the requested MIPS register. - RiscVGen::RiscVReg MapFPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); - - void MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad = true); - RiscVGen::RiscVReg MapDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad = true); - void Map4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad = true); - RiscVGen::RiscVReg Map4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad = true); - void FlushBeforeCall(); - - RiscVGen::RiscVReg R(IRReg preg); // Returns a cached register - -protected: - void SetupInitialRegs() override; - const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const override; - - void LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; - void StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; - void SetNativeRegValue(IRNativeReg nreg, uint32_t imm) override; - void StoreRegValue(IRReg mreg, uint32_t imm) override; - -private: - RiscVGen::RiscVEmitter *emit_ = nullptr; - - enum { - NUM_RVFPUREG = 32, - }; -}; From e40ae600291c1e654d4e06c58d15e99db2fa1ffa Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 00:58:24 -0700 Subject: [PATCH 08/36] riscv: Mark normalized32 after mapping. It's less confusing to separate it. --- Core/MIPS/IR/IRRegCache.cpp | 6 ++---- Core/MIPS/IR/IRRegCache.h | 1 - Core/MIPS/RiscV/RiscVCompALU.cpp | 8 ++++++-- Core/MIPS/RiscV/RiscVCompFPU.cpp | 21 ++++++++++++++------- Core/MIPS/RiscV/RiscVCompLoadStore.cpp | 3 ++- Core/MIPS/RiscV/RiscVCompSystem.cpp | 17 ++++++++++------- Core/MIPS/RiscV/RiscVRegCache.cpp | 8 +++----- Core/MIPS/RiscV/RiscVRegCache.h | 1 - 8 files changed, 37 insertions(+), 28 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index 4ee289f6ac..8bad476dac 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -844,7 +844,7 @@ void IRNativeRegCacheBase::MapNativeReg(MIPSLoc type, IRNativeReg nreg, IRReg fi if (mr[first].nReg != nreg) { nr[nreg].isDirty = markDirty; nr[nreg].pointerified = false; - nr[nreg].normalized32 = (flags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; + nr[nreg].normalized32 = false; } // Alright, now to actually map. @@ -920,10 +920,8 @@ void IRNativeRegCacheBase::MapNativeReg(MIPSLoc type, IRNativeReg nreg, IRReg fi if (markDirty) { nr[nreg].isDirty = true; nr[nreg].pointerified = false; - nr[nreg].normalized32 = (flags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32; + nr[nreg].normalized32 = false; _assert_(first != MIPS_REG_ZERO); - } else if ((flags & MIPSMap::MARK_NORM32) == MIPSMap::MARK_NORM32) { - nr[nreg].normalized32 = true; } } diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index 7b8a627773..85e3eae7c5 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -82,7 +82,6 @@ enum class MIPSMap { INIT = 0, DIRTY = 1, NOINIT = 2 | DIRTY, - MARK_NORM32 = 4, }; static inline MIPSMap operator |(const MIPSMap &lhs, const MIPSMap &rhs) { return MIPSMap((int)lhs | (int)rhs); diff --git a/Core/MIPS/RiscV/RiscVCompALU.cpp b/Core/MIPS/RiscV/RiscVCompALU.cpp index eb0cc6dc79..753eb32a9d 100644 --- a/Core/MIPS/RiscV/RiscVCompALU.cpp +++ b/Core/MIPS/RiscV/RiscVCompALU.cpp @@ -697,7 +697,7 @@ void RiscVJitBackend::CompIR_Div(IRInst inst) { RiscVReg numReg, denomReg; switch (inst.op) { case IROp::Div: - regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2); // We have to do this because of the divide by zero and overflow checks below. NormalizeSrc12(inst, &numReg, &denomReg, SCRATCH1, SCRATCH2, true); DIVW(regs_.R(IRREG_LO), numReg, denomReg); @@ -722,10 +722,12 @@ void RiscVJitBackend::CompIR_Div(IRInst inst) { SetJumpTarget(notNegativeOne); SetJumpTarget(notMostNegative); } + regs_.MarkGPRDirty(IRREG_LO, true); + regs_.MarkGPRDirty(IRREG_HI, true); break; case IROp::DivU: - regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32); + regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2); // We have to do this because of the divide by zero check below. NormalizeSrc12(inst, &numReg, &denomReg, SCRATCH1, SCRATCH2, true); DIVUW(regs_.R(IRREG_LO), numReg, denomReg); @@ -741,6 +743,8 @@ void RiscVJitBackend::CompIR_Div(IRInst inst) { SetJumpTarget(keepNegOne); SetJumpTarget(skipNonZero); } + regs_.MarkGPRDirty(IRREG_LO, true); + regs_.MarkGPRDirty(IRREG_HI, true); break; default: diff --git a/Core/MIPS/RiscV/RiscVCompFPU.cpp b/Core/MIPS/RiscV/RiscVCompFPU.cpp index bdc2def130..0ca4accc54 100644 --- a/Core/MIPS/RiscV/RiscVCompFPU.cpp +++ b/Core/MIPS/RiscV/RiscVCompFPU.cpp @@ -351,24 +351,26 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { case IRFpCompareMode::EitherUnordered: regs_.Map(inst); - regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT); FCLASS(32, SCRATCH1, regs_.F(inst.src1)); FCLASS(32, SCRATCH2, regs_.F(inst.src2)); OR(SCRATCH1, SCRATCH1, SCRATCH2); // NAN is 0x100 or 0x200. ANDI(SCRATCH1, SCRATCH1, 0x300); SNEZ(regs_.R(IRREG_FPCOND), SCRATCH1); + regs_.MarkGPRDirty(IRREG_FPCOND, true); break; case IRFpCompareMode::EqualOrdered: regs_.Map(inst); - regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT); FEQ(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2)); + regs_.MarkGPRDirty(IRREG_FPCOND, true); break; case IRFpCompareMode::EqualUnordered: regs_.Map(inst); - regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT); FEQ(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2)); // Now let's just OR in the unordered check. @@ -379,32 +381,37 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { ANDI(SCRATCH1, SCRATCH1, 0x300); SNEZ(SCRATCH1, SCRATCH1); OR(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND), SCRATCH1); + regs_.MarkGPRDirty(IRREG_FPCOND, true); break; case IRFpCompareMode::LessEqualOrdered: regs_.Map(inst); - regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT); FLE(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2)); + regs_.MarkGPRDirty(IRREG_FPCOND, true); break; case IRFpCompareMode::LessEqualUnordered: regs_.Map(inst); - regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT); FLT(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src2), regs_.F(inst.src1)); SEQZ(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND)); + regs_.MarkGPRDirty(IRREG_FPCOND, true); break; case IRFpCompareMode::LessOrdered: regs_.Map(inst); - regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT); FLT(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2)); + regs_.MarkGPRDirty(IRREG_FPCOND, true); break; case IRFpCompareMode::LessUnordered: regs_.Map(inst); - regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT); FLE(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src2), regs_.F(inst.src1)); SEQZ(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND)); + regs_.MarkGPRDirty(IRREG_FPCOND, true); break; } break; diff --git a/Core/MIPS/RiscV/RiscVCompLoadStore.cpp b/Core/MIPS/RiscV/RiscVCompLoadStore.cpp index e21c43ab9f..08f5cb88e2 100644 --- a/Core/MIPS/RiscV/RiscVCompLoadStore.cpp +++ b/Core/MIPS/RiscV/RiscVCompLoadStore.cpp @@ -81,7 +81,8 @@ void RiscVJitBackend::CompIR_Load(IRInst inst) { addrReg = SCRATCH1; } // With NOINIT, MapReg won't subtract MEMBASEREG even if dest == src1. - regs_.MapGPR(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + regs_.MapGPR(inst.dest, MIPSMap::NOINIT); + regs_.MarkGPRDirty(inst.dest, true); s32 imm = AdjustForAddressOffset(&addrReg, inst.constant); diff --git a/Core/MIPS/RiscV/RiscVCompSystem.cpp b/Core/MIPS/RiscV/RiscVCompSystem.cpp index c7dbb531db..7b894da353 100644 --- a/Core/MIPS/RiscV/RiscVCompSystem.cpp +++ b/Core/MIPS/RiscV/RiscVCompSystem.cpp @@ -100,9 +100,10 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { break; case IROp::SetCtrlVFPUFReg: - regs_.MapGPR(IRREG_VFPU_CTRL_BASE + inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); + regs_.MapGPR(IRREG_VFPU_CTRL_BASE + inst.dest, MIPSMap::NOINIT); regs_.MapFPR(inst.src1); FMV(FMv::X, FMv::W, regs_.R(IRREG_VFPU_CTRL_BASE + inst.dest), regs_.F(inst.src1)); + regs_.MarkGPRDirty(IRREG_VFPU_CTRL_BASE + inst.dest, true); break; case IROp::FpCondFromReg: @@ -117,7 +118,7 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { break; case IROp::FpCtrlFromReg: - regs_.MapGPRDirtyIn(IRREG_FPCOND, inst.src1, MapType::AVOID_LOAD_MARK_NORM32); + regs_.MapGPRDirtyIn(IRREG_FPCOND, inst.src1); LI(SCRATCH1, 0x0181FFFF); AND(SCRATCH1, regs_.R(inst.src1), SCRATCH1); // Extract the new fpcond value. @@ -128,10 +129,11 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { ANDI(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND), 1); } SW(SCRATCH1, CTXREG, IRREG_FCR31 * 4); + regs_.MarkGPRDirty(IRREG_FPCOND, true); break; case IROp::FpCtrlToReg: - regs_.MapGPRDirtyIn(inst.dest, IRREG_FPCOND, MapType::AVOID_LOAD_MARK_NORM32); + regs_.MapGPRDirtyIn(inst.dest, IRREG_FPCOND); // Load fcr31 and clear the fpcond bit. LW(SCRATCH1, CTXREG, IRREG_FCR31 * 4); if (cpu_info.RiscV_Zbs) { @@ -148,6 +150,7 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { // Also update mips->fcr31 while we're here. SW(regs_.R(inst.dest), CTXREG, IRREG_FCR31 * 4); + regs_.MarkGPRDirty(inst.dest, true); break; case IROp::VfpuCtrlToReg: @@ -157,19 +160,19 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { break; case IROp::FMovFromGPR: - regs_.MapFPR(inst.dest, MIPSMap::NOINIT); if (regs_.IsGPRImm(inst.src1) && regs_.GetGPRImm(inst.src1) == 0) { + regs_.MapFPR(inst.dest, MIPSMap::NOINIT); FCVT(FConv::S, FConv::W, regs_.F(inst.dest), R_ZERO); } else { - regs_.MapGPR(inst.src1); + regs_.Map(inst); FMV(FMv::W, FMv::X, regs_.F(inst.dest), regs_.R(inst.src1)); } break; case IROp::FMovToGPR: - regs_.MapGPR(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32); - regs_.MapFPR(inst.src1); + regs_.Map(inst); FMV(FMv::X, FMv::W, regs_.R(inst.dest), regs_.F(inst.src1)); + regs_.MarkGPRDirty(inst.dest, true); break; default: diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index f172b6ef67..c2949222c7 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -258,8 +258,7 @@ RiscVReg RiscVRegCache::MapGPRAsPointer(IRReg reg) { void RiscVRegCache::MapGPRDirtyIn(IRReg rd, IRReg rs, MapType type) { SpillLockGPR(rd, rs); bool load = type == MapType::ALWAYS_LOAD || rd == rs; - MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; - MapGPR(rd, (load ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); + MapGPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); MapGPR(rs); ReleaseSpillLockGPR(rd, rs); } @@ -268,9 +267,8 @@ void RiscVRegCache::MapGPRDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg r SpillLockGPR(rd1, rd2, rs, rt); bool load1 = type == MapType::ALWAYS_LOAD || (rd1 == rs || rd1 == rt); bool load2 = type == MapType::ALWAYS_LOAD || (rd2 == rs || rd2 == rt); - MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT; - MapGPR(rd1, (load1 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); - MapGPR(rd2, (load2 ? MIPSMap::DIRTY : MIPSMap::NOINIT) | norm32); + MapGPR(rd1, load1 ? MIPSMap::DIRTY : MIPSMap::NOINIT); + MapGPR(rd2, load2 ? MIPSMap::DIRTY : MIPSMap::NOINIT); MapGPR(rt); MapGPR(rs); ReleaseSpillLockGPR(rd1, rd2, rs, rt); diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index 26cf0d9894..ed3a209ee5 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -37,7 +37,6 @@ const RiscVGen::RiscVReg SCRATCH2 = RiscVGen::X11; enum class MapType { AVOID_LOAD, - AVOID_LOAD_MARK_NORM32, ALWAYS_LOAD, }; From cc4bc406d501ec419eb3a16d68548673713272cb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 10:56:46 -0700 Subject: [PATCH 09/36] riscv: Cleanup VfpuCtrlToReg meta, use auto-map. --- Core/MIPS/IR/IRInst.cpp | 2 +- Core/MIPS/RiscV/RiscVCompALU.cpp | 8 ++++---- Core/MIPS/RiscV/RiscVCompSystem.cpp | 7 +++---- Core/MIPS/RiscV/RiscVRegCache.cpp | 10 +++++----- Core/MIPS/RiscV/RiscVRegCache.h | 9 ++------- 5 files changed, 15 insertions(+), 21 deletions(-) diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index bc7d9f4a9a..ca798afb5d 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -117,7 +117,7 @@ static const IRMeta irMeta[] = { { IROp::FpCondToReg, "FpCondToReg", "G" }, { IROp::FpCtrlFromReg, "FpCtrlFromReg", "_G" }, { IROp::FpCtrlToReg, "FpCtrlToReg", "G" }, - { IROp::VfpuCtrlToReg, "VfpuCtrlToReg", "GI" }, + { IROp::VfpuCtrlToReg, "VfpuCtrlToReg", "GT" }, { IROp::SetCtrlVFPU, "SetCtrlVFPU", "TC" }, { IROp::SetCtrlVFPUReg, "SetCtrlVFPUReg", "TG" }, { IROp::SetCtrlVFPUFReg, "SetCtrlVFPUFReg", "TF" }, diff --git a/Core/MIPS/RiscV/RiscVCompALU.cpp b/Core/MIPS/RiscV/RiscVCompALU.cpp index 753eb32a9d..5aa944034c 100644 --- a/Core/MIPS/RiscV/RiscVCompALU.cpp +++ b/Core/MIPS/RiscV/RiscVCompALU.cpp @@ -646,7 +646,7 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) { break; case IROp::Madd: - regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); + regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, false); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); MUL(SCRATCH1, lhs, rhs); @@ -656,7 +656,7 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) { break; case IROp::MaddU: - regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); + regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, false); makeArgsUnsigned(&lhs, &rhs); MUL(SCRATCH1, lhs, rhs); @@ -666,7 +666,7 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) { break; case IROp::Msub: - regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); + regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, false); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); MUL(SCRATCH1, lhs, rhs); @@ -676,7 +676,7 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) { break; case IROp::MsubU: - regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD); + regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, false); makeArgsUnsigned(&lhs, &rhs); MUL(SCRATCH1, lhs, rhs); diff --git a/Core/MIPS/RiscV/RiscVCompSystem.cpp b/Core/MIPS/RiscV/RiscVCompSystem.cpp index 7b894da353..2dce06d3b9 100644 --- a/Core/MIPS/RiscV/RiscVCompSystem.cpp +++ b/Core/MIPS/RiscV/RiscVCompSystem.cpp @@ -94,14 +94,13 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { break; case IROp::SetCtrlVFPUReg: - regs_.MapGPRDirtyIn(IRREG_VFPU_CTRL_BASE + inst.dest, inst.src1); + regs_.Map(inst); MV(regs_.R(IRREG_VFPU_CTRL_BASE + inst.dest), regs_.R(inst.src1)); regs_.MarkGPRDirty(IRREG_VFPU_CTRL_BASE + inst.dest, regs_.IsNormalized32(inst.src1)); break; case IROp::SetCtrlVFPUFReg: - regs_.MapGPR(IRREG_VFPU_CTRL_BASE + inst.dest, MIPSMap::NOINIT); - regs_.MapFPR(inst.src1); + regs_.Map(inst); FMV(FMv::X, FMv::W, regs_.R(IRREG_VFPU_CTRL_BASE + inst.dest), regs_.F(inst.src1)); regs_.MarkGPRDirty(IRREG_VFPU_CTRL_BASE + inst.dest, true); break; @@ -154,7 +153,7 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { break; case IROp::VfpuCtrlToReg: - regs_.MapGPRDirtyIn(inst.dest, IRREG_VFPU_CTRL_BASE + inst.src1); + regs_.Map(inst); MV(regs_.R(inst.dest), regs_.R(IRREG_VFPU_CTRL_BASE + inst.src1)); regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_VFPU_CTRL_BASE + inst.src1)); break; diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index c2949222c7..cd1ffc0c7a 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -255,18 +255,18 @@ RiscVReg RiscVRegCache::MapGPRAsPointer(IRReg reg) { return (RiscVReg)MapNativeRegAsPointer(reg); } -void RiscVRegCache::MapGPRDirtyIn(IRReg rd, IRReg rs, MapType type) { +void RiscVRegCache::MapGPRDirtyIn(IRReg rd, IRReg rs, bool avoidLoad) { SpillLockGPR(rd, rs); - bool load = type == MapType::ALWAYS_LOAD || rd == rs; + bool load = !avoidLoad || rd == rs; MapGPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); MapGPR(rs); ReleaseSpillLockGPR(rd, rs); } -void RiscVRegCache::MapGPRDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, MapType type) { +void RiscVRegCache::MapGPRDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, bool avoidLoad) { SpillLockGPR(rd1, rd2, rs, rt); - bool load1 = type == MapType::ALWAYS_LOAD || (rd1 == rs || rd1 == rt); - bool load2 = type == MapType::ALWAYS_LOAD || (rd2 == rs || rd2 == rt); + bool load1 = !avoidLoad || (rd1 == rs || rd1 == rt); + bool load2 = !avoidLoad || (rd2 == rs || rd2 == rt); MapGPR(rd1, load1 ? MIPSMap::DIRTY : MIPSMap::NOINIT); MapGPR(rd2, load2 ? MIPSMap::DIRTY : MIPSMap::NOINIT); MapGPR(rt); diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index ed3a209ee5..ccaf37a689 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -35,11 +35,6 @@ const RiscVGen::RiscVReg MEMBASEREG = RiscVGen::X27; const RiscVGen::RiscVReg SCRATCH1 = RiscVGen::X10; const RiscVGen::RiscVReg SCRATCH2 = RiscVGen::X11; -enum class MapType { - AVOID_LOAD, - ALWAYS_LOAD, -}; - } // namespace RiscVJitConstants class RiscVRegCache : public IRNativeRegCacheBase { @@ -55,8 +50,8 @@ public: RiscVGen::RiscVReg MapGPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); RiscVGen::RiscVReg MapGPRAsPointer(IRReg reg); - void MapGPRDirtyIn(IRReg rd, IRReg rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); - void MapGPRDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD); + void MapGPRDirtyIn(IRReg rd, IRReg rs, bool avoidLoad = true); + void MapGPRDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, bool avoidLoad = true); // Returns a RISC-V register containing the requested MIPS register. RiscVGen::RiscVReg MapFPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); From a190793ad27ae38c403cd821e5ec07ad38ad1c81 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 11:29:01 -0700 Subject: [PATCH 10/36] riscv: Simplify mapping for more instructions. --- Core/MIPS/IR/IRRegCache.cpp | 195 +++++++++++++++++++----------- Core/MIPS/IR/IRRegCache.h | 22 +++- Core/MIPS/RiscV/RiscVCompFPU.cpp | 12 +- Core/MIPS/RiscV/RiscVCompVec.cpp | 2 +- Core/MIPS/RiscV/RiscVRegCache.cpp | 28 +---- Core/MIPS/RiscV/RiscVRegCache.h | 4 +- 6 files changed, 157 insertions(+), 106 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index 8bad476dac..486726a8fb 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -282,6 +282,11 @@ void IRNativeRegCacheBase::SetSpillLockIRIndex(IRReg r1, IRReg r2, IRReg r3, IRR mr[r4 + offset].spillLockIRIndex = index; } +void IRNativeRegCacheBase::SetSpillLockIRIndex(IRReg r1, int index) { + if (!mr[r1].isStatic) + mr[r1].spillLockIRIndex = index; +} + void IRNativeRegCacheBase::MarkGPRDirty(IRReg gpr, bool andNormalized32) { _assert_(IsGPRMapped(gpr)); if (!IsGPRMapped(gpr)) @@ -631,35 +636,132 @@ void IRNativeRegCacheBase::FlushAll(bool gprs, bool fprs) { } void IRNativeRegCacheBase::Map(const IRInst &inst) { + Mapping mapping[3]; + MappingFromInst(inst, mapping); + + ApplyMapping(mapping, 3); + CleanupMapping(mapping, 3); +} + +IRNativeReg IRNativeRegCacheBase::MapWithTemp(const IRInst &inst, MIPSLoc type) { + Mapping mapping[3]; + MappingFromInst(inst, mapping); + + ApplyMapping(mapping, 3); + // Grab a temp while things are spill locked. + IRNativeReg temp = AllocateReg(type); + CleanupMapping(mapping, 3); + return temp; +} + +void IRNativeRegCacheBase::ApplyMapping(const Mapping *mapping, int count) { + for (int i = 0; i < count; ++i) { + SetSpillLockIRIndex(mapping[i].reg, irIndex_); + if (!config_.mapSIMD) { + for (int j = 1; j < mapping[i].lanes; ++j) + SetSpillLockIRIndex(mapping[i].reg + j, irIndex_); + } + } + + auto mapRegs = [&](int i) { + MIPSLoc type = MIPSLoc::MEM; + switch (mapping[i].type) { + case 'G': type = MIPSLoc::REG; break; + case 'F': type = MIPSLoc::FREG; break; + case 'V': type = MIPSLoc::VREG; break; + + case '_': + // Ignored intentionally. + return; + + default: + _assert_msg_(false, "Unexpected type: %c", mapping[i].type); + return; + } + + if (config_.mapSIMD) { + MapNativeReg(type, mapping[i].reg, mapping[i].lanes, mapping[i].flags); + return; + } + + for (int j = 0; j < mapping[i].lanes; ++j) + MapNativeReg(type, mapping[i].reg + j, 1, mapping[i].flags); + }; + + // Do two passes: first any without NOINIT, then NOINIT. + for (int i = 0; i < count; ++i) { + if ((mapping[i].flags & MIPSMap::NOINIT) != MIPSMap::NOINIT) + mapRegs(i); + } + for (int i = 0; i < count; ++i) { + if ((mapping[i].flags & MIPSMap::NOINIT) == MIPSMap::NOINIT) + mapRegs(i); + } +} + +void IRNativeRegCacheBase::CleanupMapping(const Mapping *mapping, int count) { + for (int i = 0; i < count; ++i) { + SetSpillLockIRIndex(mapping[i].reg, -1); + if (!config_.mapSIMD) { + for (int j = 1; j < mapping[i].lanes; ++j) + SetSpillLockIRIndex(mapping[i].reg + j, -1); + } + } + + // Sanity check. If these don't pass, we may have Vec overlap issues or etc. + for (int i = 0; i < count; ++i) { + if (mapping[i].reg != IRREG_INVALID) { + auto &mreg = mr[mapping[i].reg]; + _dbg_assert_(mreg.nReg != -1); + if (mapping[i].type == 'G') { + _dbg_assert_(mreg.loc == MIPSLoc::REG || mreg.loc == MIPSLoc::REG_AS_PTR || mreg.loc == MIPSLoc::REG_IMM); + } else if (mapping[i].type == 'F') { + _dbg_assert_(mreg.loc == MIPSLoc::FREG); + } else if (mapping[i].type == 'V') { + _dbg_assert_(mreg.loc == MIPSLoc::VREG); + } + if (mapping[i].lanes != 1 && config_.mapSIMD) { + _dbg_assert_(mreg.lane == 0); + _dbg_assert_(mr[mapping[i].reg + mapping[i].lanes - 1].lane == mapping[i].lanes - 1); + _dbg_assert_(mreg.nReg == mr[mapping[i].reg + mapping[i].lanes - 1].nReg); + } else { + _dbg_assert_(mreg.lane == -1); + } + } + } +} + +void IRNativeRegCacheBase::MappingFromInst(const IRInst &inst, Mapping mapping[3]) { + mapping[0].reg = inst.dest; + mapping[1].reg = inst.src1; + mapping[2].reg = inst.src2; + const IRMeta *m = GetIRMeta(inst.op); - IRReg regs[3]{ inst.dest, inst.src1, inst.src2 }; - MIPSLoc regTypes[3]{ MIPSLoc::MEM, MIPSLoc::MEM, MIPSLoc::MEM }; - int regLanes[3]{ 1, 1, 1 }; for (int i = 0; i < 3; ++i) { switch (m->types[i]) { case 'G': - regTypes[i] = MIPSLoc::REG; - _assert_msg_(IsValidGPR(regs[i]), "G was not valid GPR?"); + mapping[i].type = 'G'; + _assert_msg_(IsValidGPR(mapping[i].reg), "G was not valid GPR?"); break; case 'F': - regs[i] += 32; - regTypes[i] = MIPSLoc::FREG; - _assert_msg_(IsValidFPR(regs[i] - 32), "F was not valid FPR?"); + mapping[i].reg += 32; + mapping[i].type = 'F'; + _assert_msg_(IsValidFPR(mapping[i].reg - 32), "F was not valid FPR?"); break; case 'V': case '2': - regs[i] += 32; - regTypes[i] = config_.mapUseVRegs ? MIPSLoc::VREG : MIPSLoc::FREG; - regLanes[i] = m->types[i] == 'V' ? 4 : (m->types[i] == '2' ? 2 : 1); - _assert_msg_(IsValidFPR(regs[i] - 32), "%c was not valid FPR?", m->types[i]); + mapping[i].reg += 32; + mapping[i].type = config_.mapUseVRegs ? 'V' : 'F'; + mapping[i].lanes = m->types[i] == 'V' ? 4 : (m->types[i] == '2' ? 2 : 1); + _assert_msg_(IsValidFPR(mapping[i].reg - 32), "%c was not valid FPR?", m->types[i]); break; case 'T': - regTypes[i] = MIPSLoc::REG; - _assert_msg_(regs[i] < VFPU_CTRL_MAX, "T was not valid VFPU CTRL?"); - regs[i] += IRREG_VFPU_CTRL_BASE; + mapping[i].type = 'G'; + _assert_msg_(mapping[i].reg < VFPU_CTRL_MAX, "T was not valid VFPU CTRL?"); + mapping[i].reg += IRREG_VFPU_CTRL_BASE; break; case '\0': @@ -669,69 +771,24 @@ void IRNativeRegCacheBase::Map(const IRInst &inst) { case 'v': case 's': case 'm': - regs[i] = IRREG_INVALID; - regLanes[i] = 0; + mapping[i].type = '_'; + mapping[i].reg = IRREG_INVALID; + mapping[i].lanes = 0; break; default: - _assert_msg_(regs[i] == IRREG_INVALID, "Unexpected register type %c", m->types[i]); + _assert_msg_(mapping[i].reg == IRREG_INVALID, "Unexpected register type %c", m->types[i]); break; } } - SetSpillLockIRIndex(regs[0], regs[1], regs[2], IRREG_INVALID, 0, irIndex_); - for (int i = 0; i < 3 && !config_.mapSIMD; ++i) { - for (int j = 1; j < regLanes[i]; ++j) - SetSpillLockIRIndex(regs[i] + j, IRREG_INVALID, IRREG_INVALID, IRREG_INVALID, 0, irIndex_); - } - - auto mapRegs = [&](int i, MIPSMap flags) { - if (config_.mapSIMD) { - MapNativeReg(regTypes[i], regs[i], regLanes[i], flags); - return; - } - - for (int j = 0; j < regLanes[i]; ++j) - MapNativeReg(regTypes[i], regs[i] + j, 1, flags); - }; - - if (regs[1] != IRREG_INVALID) - mapRegs(1, MIPSMap::INIT); - if (regs[2] != IRREG_INVALID) - mapRegs(2, MIPSMap::INIT); - if (regs[0] != IRREG_INVALID) { - MIPSMap flags = MIPSMap::NOINIT; + if (mapping[0].type != '_') { if ((m->flags & IRFLAG_SRC3DST) != 0) - flags = MIPSMap::DIRTY; + mapping[0].flags = MIPSMap::DIRTY; else if ((m->flags & IRFLAG_SRC3) != 0) - flags = MIPSMap::INIT; - - mapRegs(0, flags); - } - - SetSpillLockIRIndex(regs[0], regs[1], regs[2], IRREG_INVALID, 0, -1); - for (int i = 0; i < 3 && !config_.mapSIMD; ++i) { - for (int j = 1; j < regLanes[i]; ++j) - SetSpillLockIRIndex(regs[i] + j, IRREG_INVALID, IRREG_INVALID, IRREG_INVALID, 0, -1); - } - - // Sanity check. If these don't pass, we may have Vec overlap issues or etc. - for (int i = 0; i < 3; ++i) { - if (regs[i] != IRREG_INVALID) { - _dbg_assert_(mr[regs[i]].nReg != -1); - if (regTypes[i] == MIPSLoc::REG) { - _dbg_assert_(mr[regs[i]].loc == MIPSLoc::REG || mr[regs[i]].loc == MIPSLoc::REG_AS_PTR || mr[regs[i]].loc == MIPSLoc::REG_IMM); - } else { - _dbg_assert_(mr[regs[i]].loc == regTypes[i]); - } - if (regLanes[i] != 1 && config_.mapSIMD) { - _dbg_assert_(mr[regs[i]].lane == 0); - _dbg_assert_(mr[regs[i] + regLanes[i] - 1].lane == regLanes[i] - 1); - _dbg_assert_(mr[regs[i]].nReg == mr[regs[i] + regLanes[i] - 1].nReg); - } else { - _dbg_assert_(mr[regs[i]].lane == -1); - } - } + mapping[0].flags = MIPSMap::INIT; + else + mapping[0].flags = MIPSMap::NOINIT; } } diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index 85e3eae7c5..c4b8fa49af 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -78,16 +78,16 @@ private: }; // Initing is the default so the flag is reversed. -enum class MIPSMap { +enum class MIPSMap : uint8_t { INIT = 0, DIRTY = 1, NOINIT = 2 | DIRTY, }; static inline MIPSMap operator |(const MIPSMap &lhs, const MIPSMap &rhs) { - return MIPSMap((int)lhs | (int)rhs); + return MIPSMap((uint8_t)lhs | (uint8_t)rhs); } static inline MIPSMap operator &(const MIPSMap &lhs, const MIPSMap &rhs) { - return MIPSMap((int)lhs & (int)rhs); + return MIPSMap((uint8_t)lhs & (uint8_t)rhs); } class IRNativeRegCacheBase { @@ -182,7 +182,14 @@ public: void MarkGPRDirty(IRReg gpr, bool andNormalized32 = false); void MarkGPRAsPointerDirty(IRReg gpr); - virtual void Map(const IRInst &inst); + struct Mapping { + char type = '?'; + IRReg reg = IRREG_INVALID; + uint8_t lanes = 1; + MIPSMap flags = MIPSMap::INIT; + }; + + void Map(const IRInst &inst); virtual void FlushAll(bool gprs = true, bool fprs = true); protected: @@ -205,6 +212,12 @@ protected: virtual IRNativeReg MapNativeReg(MIPSLoc type, IRReg first, int lanes, MIPSMap flags); IRNativeReg MapNativeRegAsPointer(IRReg gpr); + IRNativeReg MapWithTemp(const IRInst &inst, MIPSLoc type); + + void MappingFromInst(const IRInst &inst, Mapping mapping[3]); + void ApplyMapping(const Mapping *mapping, int count); + void CleanupMapping(const Mapping *mapping, int count); + // Load data from memory (possibly multiple lanes) into a native reg. virtual void LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) = 0; // Store data in a native reg back into memory. @@ -215,6 +228,7 @@ protected: virtual void StoreRegValue(IRReg mreg, uint32_t imm) = 0; void SetSpillLockIRIndex(IRReg reg, IRReg reg2, IRReg reg3, IRReg reg4, int offset, int index); + void SetSpillLockIRIndex(IRReg reg, int index); int GetMipsRegOffset(IRReg r); bool IsValidGPR(IRReg r) const; diff --git a/Core/MIPS/RiscV/RiscVCompFPU.cpp b/Core/MIPS/RiscV/RiscVCompFPU.cpp index 0ca4accc54..8ad5d74a32 100644 --- a/Core/MIPS/RiscV/RiscVCompFPU.cpp +++ b/Core/MIPS/RiscV/RiscVCompFPU.cpp @@ -243,7 +243,7 @@ void RiscVJitBackend::CompIR_FCvt(IRInst inst) { case 3: rm = Round::DOWN; break; } - tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1); + tempReg = regs_.MapWithFPRTemp(inst); // Prepare the double src1 and the multiplier. FCVT(FConv::D, FConv::S, regs_.F(inst.dest), regs_.F(inst.src1)); LI(SCRATCH1, 1UL << (inst.src2 & 0x1F)); @@ -261,7 +261,7 @@ void RiscVJitBackend::CompIR_FCvt(IRInst inst) { case IROp::FCvtScaledSW: // TODO: This is probably proceeded by a GPR transfer, might be ideal to combine. - tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1); + tempReg = regs_.MapWithFPRTemp(inst); FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1)); FCVT(FConv::S, FConv::W, regs_.F(inst.dest), SCRATCH1); @@ -286,7 +286,7 @@ void RiscVJitBackend::CompIR_FSat(IRInst inst) { FixupBranch skipHigher; switch (inst.op) { case IROp::FSat0_1: - tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1); + tempReg = regs_.MapWithFPRTemp(inst); if (inst.dest != inst.src1) FMV(32, regs_.F(inst.dest), regs_.F(inst.src1)); @@ -312,7 +312,7 @@ void RiscVJitBackend::CompIR_FSat(IRInst inst) { break; case IROp::FSatMinus1_1: - tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1); + tempReg = regs_.MapWithFPRTemp(inst); if (inst.dest != inst.src1) FMV(32, regs_.F(inst.dest), regs_.F(inst.src1)); @@ -616,7 +616,7 @@ void RiscVJitBackend::CompIR_FSpecial(IRInst inst) { break; case IROp::FRSqrt: - tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1); + tempReg = regs_.MapWithFPRTemp(inst); FSQRT(32, regs_.F(inst.dest), regs_.F(inst.src1)); // Ugh, we can't really avoid a temp here. Probably not worth a permanent one. @@ -633,7 +633,7 @@ void RiscVJitBackend::CompIR_FSpecial(IRInst inst) { FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1); FDIV(32, regs_.F(inst.dest), regs_.F(inst.dest), regs_.F(inst.src1)); } else { - tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1); + tempReg = regs_.MapWithFPRTemp(inst); LI(SCRATCH1, 1.0f); FMV(FMv::W, FMv::X, tempReg, SCRATCH1); FDIV(32, regs_.F(inst.dest), tempReg, regs_.F(inst.src1)); diff --git a/Core/MIPS/RiscV/RiscVCompVec.cpp b/Core/MIPS/RiscV/RiscVCompVec.cpp index 7c85b26162..a29cfab8a9 100644 --- a/Core/MIPS/RiscV/RiscVCompVec.cpp +++ b/Core/MIPS/RiscV/RiscVCompVec.cpp @@ -107,7 +107,7 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) { case IROp::Vec4Shuffle: if (inst.dest == inst.src1) { - RiscVReg tempReg = regs_.MapFPR4DirtyInTemp(inst.dest, inst.src1); + RiscVReg tempReg = regs_.MapWithFPRTemp(inst); // Try to find the least swaps needed to move in place, never worse than 6 FMVs. // Would be better with a vmerge and vector regs. diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index cd1ffc0c7a..001ac6bd79 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -243,6 +243,10 @@ RiscVReg RiscVRegCache::GetAndLockTempR() { return reg; } +RiscVReg RiscVRegCache::MapWithFPRTemp(IRInst &inst) { + return (RiscVReg)MapWithTemp(inst, MIPSLoc::FREG); +} + RiscVReg RiscVRegCache::MapGPR(IRReg mipsReg, MIPSMap mapFlags) { _dbg_assert_(IsValidGPR(mipsReg)); @@ -293,16 +297,6 @@ void RiscVRegCache::MapFPRDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad ReleaseSpillLockFPR(rd, rs, rt); } -RiscVReg RiscVRegCache::MapFPRDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad) { - SpillLockFPR(rd, rs); - bool load = !avoidLoad || rd == rs; - MapFPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); - MapFPR(rs); - RiscVReg temp = (RiscVReg)AllocateReg(MIPSLoc::FREG); - ReleaseSpillLockFPR(rd, rs); - return temp; -} - void RiscVRegCache::MapFPR4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad) { for (int i = 0; i < 4; ++i) SpillLockFPR(rdbase + i, rsbase + i); @@ -315,20 +309,6 @@ void RiscVRegCache::MapFPR4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad) { ReleaseSpillLockFPR(rdbase + i, rsbase + i); } -RiscVReg RiscVRegCache::MapFPR4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad) { - for (int i = 0; i < 4; ++i) - SpillLockFPR(rdbase + i, rsbase + i); - bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase); - for (int i = 0; i < 4; ++i) - MapFPR(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); - for (int i = 0; i < 4; ++i) - MapFPR(rsbase + i); - RiscVReg temp = (RiscVReg)AllocateReg(MIPSLoc::FREG); - for (int i = 0; i < 4; ++i) - ReleaseSpillLockFPR(rdbase + i, rsbase + i); - return temp; -} - void RiscVRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { RiscVReg r = (RiscVReg)(X0 + nreg); _assert_(r >= X0 && r <= X31); diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index ccaf37a689..2d0f77a7ee 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -57,9 +57,9 @@ public: RiscVGen::RiscVReg MapFPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); void MapFPRDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad = true); - RiscVGen::RiscVReg MapFPRDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad = true); void MapFPR4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad = true); - RiscVGen::RiscVReg MapFPR4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad = true); + + RiscVGen::RiscVReg MapWithFPRTemp(IRInst &inst); bool IsNormalized32(IRReg reg); From 6a75e6712ef7e802e5de4dca154a8a367e33961b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 11:42:29 -0700 Subject: [PATCH 11/36] riscv: Use automapping for special cases too. --- Core/MIPS/IR/IRRegCache.cpp | 8 +++++ Core/MIPS/IR/IRRegCache.h | 1 + Core/MIPS/RiscV/RiscVCompALU.cpp | 24 +++++++------- Core/MIPS/RiscV/RiscVCompFPU.cpp | 24 +++++--------- Core/MIPS/RiscV/RiscVCompSystem.cpp | 8 ++--- Core/MIPS/RiscV/RiscVCompVec.cpp | 49 +++++++---------------------- Core/MIPS/RiscV/RiscVRegCache.cpp | 40 ----------------------- Core/MIPS/RiscV/RiscVRegCache.h | 8 ----- 8 files changed, 45 insertions(+), 117 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index 486726a8fb..18fa715c0c 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -643,6 +643,14 @@ void IRNativeRegCacheBase::Map(const IRInst &inst) { CleanupMapping(mapping, 3); } +void IRNativeRegCacheBase::MapWithExtra(const IRInst &inst, std::vector extra) { + extra.resize(extra.size() + 3); + MappingFromInst(inst, &extra[extra.size() - 3]); + + ApplyMapping(extra.data(), (int)extra.size()); + CleanupMapping(extra.data(), (int)extra.size()); +} + IRNativeReg IRNativeRegCacheBase::MapWithTemp(const IRInst &inst, MIPSLoc type) { Mapping mapping[3]; MappingFromInst(inst, mapping); diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index c4b8fa49af..cd843ee29f 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -190,6 +190,7 @@ public: }; void Map(const IRInst &inst); + void MapWithExtra(const IRInst &inst, std::vector extra); virtual void FlushAll(bool gprs = true, bool fprs = true); protected: diff --git a/Core/MIPS/RiscV/RiscVCompALU.cpp b/Core/MIPS/RiscV/RiscVCompALU.cpp index 5aa944034c..5bd3170d42 100644 --- a/Core/MIPS/RiscV/RiscVCompALU.cpp +++ b/Core/MIPS/RiscV/RiscVCompALU.cpp @@ -563,25 +563,25 @@ void RiscVJitBackend::CompIR_HiLo(IRInst inst) { switch (inst.op) { case IROp::MtLo: - regs_.MapGPRDirtyIn(IRREG_LO, inst.src1); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::NOINIT } }); MV(regs_.R(IRREG_LO), regs_.R(inst.src1)); regs_.MarkGPRDirty(IRREG_LO, regs_.IsNormalized32(inst.src1)); break; case IROp::MtHi: - regs_.MapGPRDirtyIn(IRREG_HI, inst.src1); + regs_.MapWithExtra(inst, { { 'G', IRREG_HI, 1, MIPSMap::NOINIT } }); MV(regs_.R(IRREG_HI), regs_.R(inst.src1)); regs_.MarkGPRDirty(IRREG_HI, regs_.IsNormalized32(inst.src1)); break; case IROp::MfLo: - regs_.MapGPRDirtyIn(inst.dest, IRREG_LO); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::INIT } }); MV(regs_.R(inst.dest), regs_.R(IRREG_LO)); regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_LO)); break; case IROp::MfHi: - regs_.MapGPRDirtyIn(inst.dest, IRREG_HI); + regs_.MapWithExtra(inst, { { 'G', IRREG_HI, 1, MIPSMap::INIT } }); MV(regs_.R(inst.dest), regs_.R(IRREG_HI)); regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_HI)); break; @@ -630,7 +630,7 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) { case IROp::Mult: // TODO: Maybe IR could simplify when HI is not needed or clobbered? // TODO: HI/LO merge optimization? Have to be careful of passes that split them... - regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::NOINIT }, { 'G', IRREG_HI, 1, MIPSMap::NOINIT } }); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); MUL(regs_.R(IRREG_LO), lhs, rhs); splitMulResult(); @@ -639,14 +639,14 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) { case IROp::MultU: // This is an "anti-norm32" case. Let's just zero always. // TODO: If we could know that LO was only needed, we could use MULW and be done. - regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::NOINIT }, { 'G', IRREG_HI, 1, MIPSMap::NOINIT } }); makeArgsUnsigned(&lhs, &rhs); MUL(regs_.R(IRREG_LO), lhs, rhs); splitMulResult(); break; case IROp::Madd: - regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, false); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::DIRTY }, { 'G', IRREG_HI, 1, MIPSMap::DIRTY } }); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); MUL(SCRATCH1, lhs, rhs); @@ -656,7 +656,7 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) { break; case IROp::MaddU: - regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, false); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::DIRTY }, { 'G', IRREG_HI, 1, MIPSMap::DIRTY } }); makeArgsUnsigned(&lhs, &rhs); MUL(SCRATCH1, lhs, rhs); @@ -666,7 +666,7 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) { break; case IROp::Msub: - regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, false); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::DIRTY }, { 'G', IRREG_HI, 1, MIPSMap::DIRTY } }); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); MUL(SCRATCH1, lhs, rhs); @@ -676,7 +676,7 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) { break; case IROp::MsubU: - regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, false); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::DIRTY }, { 'G', IRREG_HI, 1, MIPSMap::DIRTY } }); makeArgsUnsigned(&lhs, &rhs); MUL(SCRATCH1, lhs, rhs); @@ -697,7 +697,7 @@ void RiscVJitBackend::CompIR_Div(IRInst inst) { RiscVReg numReg, denomReg; switch (inst.op) { case IROp::Div: - regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::NOINIT }, { 'G', IRREG_HI, 1, MIPSMap::NOINIT } }); // We have to do this because of the divide by zero and overflow checks below. NormalizeSrc12(inst, &numReg, &denomReg, SCRATCH1, SCRATCH2, true); DIVW(regs_.R(IRREG_LO), numReg, denomReg); @@ -727,7 +727,7 @@ void RiscVJitBackend::CompIR_Div(IRInst inst) { break; case IROp::DivU: - regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::NOINIT }, { 'G', IRREG_HI, 1, MIPSMap::NOINIT } }); // We have to do this because of the divide by zero check below. NormalizeSrc12(inst, &numReg, &denomReg, SCRATCH1, SCRATCH2, true); DIVUW(regs_.R(IRREG_LO), numReg, denomReg); diff --git a/Core/MIPS/RiscV/RiscVCompFPU.cpp b/Core/MIPS/RiscV/RiscVCompFPU.cpp index 8ad5d74a32..c0cec762c4 100644 --- a/Core/MIPS/RiscV/RiscVCompFPU.cpp +++ b/Core/MIPS/RiscV/RiscVCompFPU.cpp @@ -350,8 +350,7 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case IRFpCompareMode::EitherUnordered: - regs_.Map(inst); - regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT); + regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } }); FCLASS(32, SCRATCH1, regs_.F(inst.src1)); FCLASS(32, SCRATCH2, regs_.F(inst.src2)); OR(SCRATCH1, SCRATCH1, SCRATCH2); @@ -362,15 +361,13 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case IRFpCompareMode::EqualOrdered: - regs_.Map(inst); - regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT); + regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } }); FEQ(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2)); regs_.MarkGPRDirty(IRREG_FPCOND, true); break; case IRFpCompareMode::EqualUnordered: - regs_.Map(inst); - regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT); + regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } }); FEQ(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2)); // Now let's just OR in the unordered check. @@ -385,30 +382,26 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case IRFpCompareMode::LessEqualOrdered: - regs_.Map(inst); - regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT); + regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } }); FLE(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2)); regs_.MarkGPRDirty(IRREG_FPCOND, true); break; case IRFpCompareMode::LessEqualUnordered: - regs_.Map(inst); - regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT); + regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } }); FLT(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src2), regs_.F(inst.src1)); SEQZ(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND)); regs_.MarkGPRDirty(IRREG_FPCOND, true); break; case IRFpCompareMode::LessOrdered: - regs_.Map(inst); - regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT); + regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } }); FLT(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2)); regs_.MarkGPRDirty(IRREG_FPCOND, true); break; case IRFpCompareMode::LessUnordered: - regs_.Map(inst); - regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT); + regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } }); FLE(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src2), regs_.F(inst.src1)); SEQZ(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND)); regs_.MarkGPRDirty(IRREG_FPCOND, true); @@ -417,8 +410,7 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) { break; case IROp::FCmovVfpuCC: - regs_.MapGPR(IRREG_VFPU_CC); - regs_.Map(inst); + regs_.MapWithExtra(inst, { { 'G', IRREG_VFPU_CC, 1, MIPSMap::INIT } }); if ((inst.src2 & 0xF) == 0) { ANDI(SCRATCH1, regs_.R(IRREG_VFPU_CC), 1); } else if (cpu_info.RiscV_Zbs) { diff --git a/Core/MIPS/RiscV/RiscVCompSystem.cpp b/Core/MIPS/RiscV/RiscVCompSystem.cpp index 2dce06d3b9..ff6de6786f 100644 --- a/Core/MIPS/RiscV/RiscVCompSystem.cpp +++ b/Core/MIPS/RiscV/RiscVCompSystem.cpp @@ -106,18 +106,18 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { break; case IROp::FpCondFromReg: - regs_.MapGPRDirtyIn(IRREG_FPCOND, inst.src1); + regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } }); MV(regs_.R(IRREG_FPCOND), regs_.R(inst.src1)); break; case IROp::FpCondToReg: - regs_.MapGPRDirtyIn(inst.dest, IRREG_FPCOND); + regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::INIT } }); MV(regs_.R(inst.dest), regs_.R(IRREG_FPCOND)); regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_FPCOND)); break; case IROp::FpCtrlFromReg: - regs_.MapGPRDirtyIn(IRREG_FPCOND, inst.src1); + regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } }); LI(SCRATCH1, 0x0181FFFF); AND(SCRATCH1, regs_.R(inst.src1), SCRATCH1); // Extract the new fpcond value. @@ -132,7 +132,7 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) { break; case IROp::FpCtrlToReg: - regs_.MapGPRDirtyIn(inst.dest, IRREG_FPCOND); + regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::INIT } }); // Load fcr31 and clear the fpcond bit. LW(SCRATCH1, CTXREG, IRREG_FCR31 * 4); if (cpu_info.RiscV_Zbs) { diff --git a/Core/MIPS/RiscV/RiscVCompVec.cpp b/Core/MIPS/RiscV/RiscVCompVec.cpp index a29cfab8a9..8db60a76a8 100644 --- a/Core/MIPS/RiscV/RiscVCompVec.cpp +++ b/Core/MIPS/RiscV/RiscVCompVec.cpp @@ -215,9 +215,8 @@ void RiscVJitBackend::CompIR_VecArith(IRInst inst) { break; case IROp::Vec4Scale: - regs_.SpillLockFPR(inst.src2); - regs_.MapFPR(inst.src2); - regs_.MapFPR4DirtyIn(inst.dest, inst.src1); + // TODO: This works for now, but may need to handle aliasing for vectors. + regs_.Map(inst); for (int i = 0; i < 4; ++i) FMUL(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i), regs_.F(inst.src2)); break; @@ -245,18 +244,8 @@ void RiscVJitBackend::CompIR_VecHoriz(IRInst inst) { switch (inst.op) { case IROp::Vec4Dot: - // TODO: Maybe some option to call the slow accurate mode? - regs_.SpillLockFPR(inst.dest); - for (int i = 0; i < 4; ++i) { - regs_.SpillLockFPR(inst.src1 + i); - regs_.SpillLockFPR(inst.src2 + i); - } - for (int i = 0; i < 4; ++i) { - regs_.MapFPR(inst.src1 + i); - regs_.MapFPR(inst.src2 + i); - } - regs_.MapFPR(inst.dest, MIPSMap::NOINIT); - + // TODO: This works for now, but may need to handle aliasing for vectors. + regs_.Map(inst); if ((inst.dest < inst.src1 + 4 && inst.dest >= inst.src1) || (inst.dest < inst.src2 + 4 && inst.dest >= inst.src2)) { // This means inst.dest overlaps one of src1 or src2. We have to do that one first. // Technically this may impact -0.0 and such, but dots accurately need to be aligned anyway. @@ -292,13 +281,8 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { break; case IROp::Vec4Unpack8To32: - regs_.SpillLockFPR(inst.src1); - for (int i = 0; i < 4; ++i) - regs_.SpillLockFPR(inst.dest + i); - regs_.MapFPR(inst.src1); - for (int i = 0; i < 4; ++i) - regs_.MapFPR(inst.dest + i, MIPSMap::NOINIT); - + // TODO: This works for now, but may need to handle aliasing for vectors. + regs_.Map(inst); FMV(FMv::X, FMv::W, SCRATCH2, regs_.F(inst.src1)); for (int i = 0; i < 4; ++i) { // Mask using walls. @@ -313,13 +297,8 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { break; case IROp::Vec2Unpack16To32: - regs_.SpillLockFPR(inst.src1); - for (int i = 0; i < 2; ++i) - regs_.SpillLockFPR(inst.dest + i); - regs_.MapFPR(inst.src1); - for (int i = 0; i < 2; ++i) - regs_.MapFPR(inst.dest + i, MIPSMap::NOINIT); - + // TODO: This works for now, but may need to handle aliasing for vectors. + regs_.Map(inst); FMV(FMv::X, FMv::W, SCRATCH2, regs_.F(inst.src1)); SLLI(SCRATCH1, SCRATCH2, 16); FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1); @@ -342,13 +321,8 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { break; case IROp::Vec4Pack31To8: - regs_.SpillLockFPR(inst.dest); - for (int i = 0; i < 4; ++i) { - regs_.SpillLockFPR(inst.src1 + i); - regs_.MapFPR(inst.src1 + i); - } - regs_.MapFPR(inst.dest, MIPSMap::NOINIT); - + // TODO: This works for now, but may need to handle aliasing for vectors. + regs_.Map(inst); for (int i = 0; i < 4; ++i) { FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1 + i)); SRLI(SCRATCH1, SCRATCH1, 23); @@ -365,7 +339,8 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) { break; case IROp::Vec2Pack32To16: - regs_.MapFPRDirtyInIn(inst.dest, inst.src1, inst.src1 + 1); + // TODO: This works for now, but may need to handle aliasing for vectors. + regs_.Map(inst); FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1)); FMV(FMv::X, FMv::W, SCRATCH2, regs_.F(inst.src1 + 1)); // Keep in mind, this was sign-extended, so we have to zero the upper. diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index 001ac6bd79..023317dc11 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -259,25 +259,6 @@ RiscVReg RiscVRegCache::MapGPRAsPointer(IRReg reg) { return (RiscVReg)MapNativeRegAsPointer(reg); } -void RiscVRegCache::MapGPRDirtyIn(IRReg rd, IRReg rs, bool avoidLoad) { - SpillLockGPR(rd, rs); - bool load = !avoidLoad || rd == rs; - MapGPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); - MapGPR(rs); - ReleaseSpillLockGPR(rd, rs); -} - -void RiscVRegCache::MapGPRDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, bool avoidLoad) { - SpillLockGPR(rd1, rd2, rs, rt); - bool load1 = !avoidLoad || (rd1 == rs || rd1 == rt); - bool load2 = !avoidLoad || (rd2 == rs || rd2 == rt); - MapGPR(rd1, load1 ? MIPSMap::DIRTY : MIPSMap::NOINIT); - MapGPR(rd2, load2 ? MIPSMap::DIRTY : MIPSMap::NOINIT); - MapGPR(rt); - MapGPR(rs); - ReleaseSpillLockGPR(rd1, rd2, rs, rt); -} - RiscVReg RiscVRegCache::MapFPR(IRReg mipsReg, MIPSMap mapFlags) { _dbg_assert_(IsValidFPR(mipsReg)); _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::MEM || mr[mipsReg + 32].loc == MIPSLoc::FREG); @@ -288,27 +269,6 @@ RiscVReg RiscVRegCache::MapFPR(IRReg mipsReg, MIPSMap mapFlags) { return INVALID_REG; } -void RiscVRegCache::MapFPRDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad) { - SpillLockFPR(rd, rs, rt); - bool load = !avoidLoad || (rd == rs || rd == rt); - MapFPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); - MapFPR(rt); - MapFPR(rs); - ReleaseSpillLockFPR(rd, rs, rt); -} - -void RiscVRegCache::MapFPR4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad) { - for (int i = 0; i < 4; ++i) - SpillLockFPR(rdbase + i, rsbase + i); - bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase); - for (int i = 0; i < 4; ++i) - MapFPR(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT); - for (int i = 0; i < 4; ++i) - MapFPR(rsbase + i); - for (int i = 0; i < 4; ++i) - ReleaseSpillLockFPR(rdbase + i, rsbase + i); -} - void RiscVRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { RiscVReg r = (RiscVReg)(X0 + nreg); _assert_(r >= X0 && r <= X31); diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index 2d0f77a7ee..4da57cc9b0 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -49,16 +49,8 @@ public: // Returns an RV register containing the requested MIPS register. RiscVGen::RiscVReg MapGPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); RiscVGen::RiscVReg MapGPRAsPointer(IRReg reg); - - void MapGPRDirtyIn(IRReg rd, IRReg rs, bool avoidLoad = true); - void MapGPRDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, bool avoidLoad = true); - - // Returns a RISC-V register containing the requested MIPS register. RiscVGen::RiscVReg MapFPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); - void MapFPRDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad = true); - void MapFPR4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad = true); - RiscVGen::RiscVReg MapWithFPRTemp(IRInst &inst); bool IsNormalized32(IRReg reg); From 36b6aa472855db0ca7b104475aadab353466cc72 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 12:40:16 -0700 Subject: [PATCH 12/36] riscv: Allow GPR "SIMD" without FPR SIMD. --- Core/MIPS/IR/IRRegCache.cpp | 8 ++++---- Core/MIPS/IR/IRRegCache.h | 2 +- Core/MIPS/RiscV/RiscVRegCache.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index 18fa715c0c..49319aa63a 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -665,7 +665,7 @@ IRNativeReg IRNativeRegCacheBase::MapWithTemp(const IRInst &inst, MIPSLoc type) void IRNativeRegCacheBase::ApplyMapping(const Mapping *mapping, int count) { for (int i = 0; i < count; ++i) { SetSpillLockIRIndex(mapping[i].reg, irIndex_); - if (!config_.mapSIMD) { + if (!config_.mapFPUSIMD && mapping[i].type != 'G') { for (int j = 1; j < mapping[i].lanes; ++j) SetSpillLockIRIndex(mapping[i].reg + j, irIndex_); } @@ -687,7 +687,7 @@ void IRNativeRegCacheBase::ApplyMapping(const Mapping *mapping, int count) { return; } - if (config_.mapSIMD) { + if (config_.mapFPUSIMD || mapping[i].type == 'G') { MapNativeReg(type, mapping[i].reg, mapping[i].lanes, mapping[i].flags); return; } @@ -710,7 +710,7 @@ void IRNativeRegCacheBase::ApplyMapping(const Mapping *mapping, int count) { void IRNativeRegCacheBase::CleanupMapping(const Mapping *mapping, int count) { for (int i = 0; i < count; ++i) { SetSpillLockIRIndex(mapping[i].reg, -1); - if (!config_.mapSIMD) { + if (!config_.mapFPUSIMD && mapping[i].type != 'G') { for (int j = 1; j < mapping[i].lanes; ++j) SetSpillLockIRIndex(mapping[i].reg + j, -1); } @@ -728,7 +728,7 @@ void IRNativeRegCacheBase::CleanupMapping(const Mapping *mapping, int count) { } else if (mapping[i].type == 'V') { _dbg_assert_(mreg.loc == MIPSLoc::VREG); } - if (mapping[i].lanes != 1 && config_.mapSIMD) { + if (mapping[i].lanes != 1 && (config_.mapFPUSIMD || mapping[i].type == 'G')) { _dbg_assert_(mreg.lane == 0); _dbg_assert_(mr[mapping[i].reg + mapping[i].lanes - 1].lane == mapping[i].lanes - 1); _dbg_assert_(mreg.nReg == mr[mapping[i].reg + mapping[i].lanes - 1].nReg); diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index cd843ee29f..8526ee7eef 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -242,7 +242,7 @@ protected: struct { int totalNativeRegs = 0; - bool mapSIMD = false; + bool mapFPUSIMD = false; bool mapUseVRegs = false; } config_; diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index 023317dc11..e7017ebd7b 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -34,7 +34,7 @@ RiscVRegCache::RiscVRegCache(MIPSComp::JitOptions *jo) // TODO: Update these when using RISC-V V. config_.totalNativeRegs = NUM_RVGPR + NUM_RVFPR; config_.mapUseVRegs = false; - config_.mapSIMD = false; + config_.mapFPUSIMD = false; } void RiscVRegCache::Init(RiscVEmitter *emitter) { From 82fb41cba06dceffc4cb9f51277814174bde842d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 13:50:02 -0700 Subject: [PATCH 13/36] irjit: Implement vtfm 4x4 using dots. --- Core/MIPS/IR/IRCompVFPU.cpp | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 0dee1eb5f2..dbb63fd04b 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1658,28 +1658,18 @@ namespace MIPSComp { } } return; - } else if (msz == M_4x4 && IsMatrixVec4(msz, sregs)) { - // Consecutive, which is harder. - DISABLE; - int s0 = IRVTEMP_0; - int s1 = IRVTEMP_PFX_S; - // Doesn't make complete sense to me why this works.... (because it doesn't.) - ir.Write(IROp::Vec4Scale, s0, sregs[0], tregs[0]); - for (int i = 1; i < 4; i++) { - if (!homogenous || (i != n - 1)) { - ir.Write(IROp::Vec4Scale, s1, sregs[i], tregs[i]); - ir.Write(IROp::Vec4Add, s0, s0, s1); - } else { - ir.Write(IROp::Vec4Add, s0, s0, sregs[i]); - } - } - if (IsVec4(sz, dregs)) { - ir.Write(IROp::Vec4Mov, dregs[0], s0); - } else { - for (int i = 0; i < 4; i++) { - ir.Write(IROp::FMov, dregs[i], s0 + i); - } + } else if (msz == M_4x4 && IsMatrixVec4(msz, sregs) && IsVec4(sz, tregs)) { + IRReg t = tregs[0]; + if (homogenous) { + // This is probably even what the hardware basically does, wiring t[3] to 1.0f. + ir.Write(IROp::Vec4Init, IRVTEMP_PFX_T, (int)Vec4Init::AllONE); + ir.Write(IROp::Vec4Blend, IRVTEMP_PFX_T, t, IRVTEMP_PFX_T, 0x7); + t = IRVTEMP_PFX_T; } + for (int i = 0; i < 4; i++) + ir.Write(IROp::Vec4Dot, IRVTEMP_PFX_D + i, sregs[i * 4], t); + for (int i = 0; i < 4; i++) + ir.Write(IROp::FMov, dregs[i], IRVTEMP_PFX_D + i); return; } From 8dfc2f04d7178817014506361a40847756dc8278 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 12:50:46 -0700 Subject: [PATCH 14/36] riscv: Use a single reg for LO/HI. This is the same optimization we have for arm64, basically. --- Core/MIPS/RiscV/RiscVCompALU.cpp | 122 ++++++++++++++++--------------- 1 file changed, 64 insertions(+), 58 deletions(-) diff --git a/Core/MIPS/RiscV/RiscVCompALU.cpp b/Core/MIPS/RiscV/RiscVCompALU.cpp index 5bd3170d42..52a13f3fd9 100644 --- a/Core/MIPS/RiscV/RiscVCompALU.cpp +++ b/Core/MIPS/RiscV/RiscVCompALU.cpp @@ -563,27 +563,43 @@ void RiscVJitBackend::CompIR_HiLo(IRInst inst) { switch (inst.op) { case IROp::MtLo: - regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::NOINIT } }); - MV(regs_.R(IRREG_LO), regs_.R(inst.src1)); - regs_.MarkGPRDirty(IRREG_LO, regs_.IsNormalized32(inst.src1)); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } }); + // First, clear the bits we're replacing. + SRLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), XLEN - 32); + SLLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), XLEN - 32); + // And now, insert the low 32 bits of src1. + if (cpu_info.RiscV_Zba) { + ADD_UW(regs_.R(IRREG_LO), regs_.R(inst.src1), regs_.R(IRREG_LO)); + } else { + SLLI(SCRATCH1, regs_.R(inst.src1), XLEN - 32); + SRLI(SCRATCH1, SCRATCH1, XLEN - 32); + ADD(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1); + } break; case IROp::MtHi: - regs_.MapWithExtra(inst, { { 'G', IRREG_HI, 1, MIPSMap::NOINIT } }); - MV(regs_.R(IRREG_HI), regs_.R(inst.src1)); - regs_.MarkGPRDirty(IRREG_HI, regs_.IsNormalized32(inst.src1)); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } }); + SLLI(SCRATCH1, regs_.R(inst.src1), XLEN - 32); + if (cpu_info.RiscV_Zba) { + ADD_UW(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1); + } else { + SLLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), XLEN - 32); + SRLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), XLEN - 32); + ADD(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1); + } break; case IROp::MfLo: - regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::INIT } }); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::INIT } }); + // It won't be normalized, but that's fine... MV(regs_.R(inst.dest), regs_.R(IRREG_LO)); - regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_LO)); break; case IROp::MfHi: - regs_.MapWithExtra(inst, { { 'G', IRREG_HI, 1, MIPSMap::INIT } }); - MV(regs_.R(inst.dest), regs_.R(IRREG_HI)); - regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_HI)); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::INIT } }); + SRAI(regs_.R(inst.dest), regs_.R(IRREG_LO), 32); + if (XLEN == 64) + regs_.MarkGPRDirty(inst.dest, true); break; default: @@ -608,81 +624,51 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) { *lhs = SCRATCH1; *rhs = SCRATCH2; }; - auto combinePrevMulResult = [&] { - // TODO: Using a single reg for HI/LO would make this less ugly. - if (cpu_info.RiscV_Zba) { - ZEXT_W(regs_.R(IRREG_LO), regs_.R(IRREG_LO)); - } else { - SLLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), XLEN - 32); - SRLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), XLEN - 32); - } - SLLI(regs_.R(IRREG_HI), regs_.R(IRREG_HI), 32); - OR(regs_.R(IRREG_LO), regs_.R(IRREG_LO), regs_.R(IRREG_HI)); - }; - auto splitMulResult = [&] { - SRAI(regs_.R(IRREG_HI), regs_.R(IRREG_LO), 32); - regs_.MarkGPRDirty(IRREG_HI, true); - }; RiscVReg lhs = INVALID_REG; RiscVReg rhs = INVALID_REG; switch (inst.op) { case IROp::Mult: // TODO: Maybe IR could simplify when HI is not needed or clobbered? - // TODO: HI/LO merge optimization? Have to be careful of passes that split them... - regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::NOINIT }, { 'G', IRREG_HI, 1, MIPSMap::NOINIT } }); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::NOINIT } }); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); MUL(regs_.R(IRREG_LO), lhs, rhs); - splitMulResult(); break; case IROp::MultU: // This is an "anti-norm32" case. Let's just zero always. - // TODO: If we could know that LO was only needed, we could use MULW and be done. - regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::NOINIT }, { 'G', IRREG_HI, 1, MIPSMap::NOINIT } }); + // TODO: If we could know that LO was only needed, we could use MULW. + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::NOINIT } }); makeArgsUnsigned(&lhs, &rhs); MUL(regs_.R(IRREG_LO), lhs, rhs); - splitMulResult(); break; case IROp::Madd: - regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::DIRTY }, { 'G', IRREG_HI, 1, MIPSMap::DIRTY } }); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } }); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); MUL(SCRATCH1, lhs, rhs); - - combinePrevMulResult(); ADD(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1); - splitMulResult(); break; case IROp::MaddU: - regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::DIRTY }, { 'G', IRREG_HI, 1, MIPSMap::DIRTY } }); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } }); makeArgsUnsigned(&lhs, &rhs); MUL(SCRATCH1, lhs, rhs); - - combinePrevMulResult(); ADD(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1); - splitMulResult(); break; case IROp::Msub: - regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::DIRTY }, { 'G', IRREG_HI, 1, MIPSMap::DIRTY } }); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } }); NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true); MUL(SCRATCH1, lhs, rhs); - - combinePrevMulResult(); SUB(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1); - splitMulResult(); break; case IROp::MsubU: - regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::DIRTY }, { 'G', IRREG_HI, 1, MIPSMap::DIRTY } }); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } }); makeArgsUnsigned(&lhs, &rhs); MUL(SCRATCH1, lhs, rhs); - - combinePrevMulResult(); SUB(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1); - splitMulResult(); break; default: @@ -697,18 +683,30 @@ void RiscVJitBackend::CompIR_Div(IRInst inst) { RiscVReg numReg, denomReg; switch (inst.op) { case IROp::Div: - regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::NOINIT }, { 'G', IRREG_HI, 1, MIPSMap::NOINIT } }); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::NOINIT } }); // We have to do this because of the divide by zero and overflow checks below. NormalizeSrc12(inst, &numReg, &denomReg, SCRATCH1, SCRATCH2, true); DIVW(regs_.R(IRREG_LO), numReg, denomReg); - REMW(regs_.R(IRREG_HI), numReg, denomReg); + REMW(R_RA, numReg, denomReg); + // Now to combine them. We'll do more with them below... + SLLI(R_RA, R_RA, 32); + if (cpu_info.RiscV_Zba) { + ADD_UW(regs_.R(IRREG_LO), regs_.R(IRREG_LO), R_RA); + } else { + SLLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), XLEN - 32); + SRLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), XLEN - 32); + ADD(regs_.R(IRREG_LO), regs_.R(IRREG_LO), R_RA); + } // Now some tweaks for divide by zero and overflow. { // Start with divide by zero, remainder is fine. FixupBranch skipNonZero = BNE(denomReg, R_ZERO); FixupBranch keepNegOne = BGE(numReg, R_ZERO); - LI(regs_.R(IRREG_LO), 1); + // Clear the -1 and replace it with 1. + SRLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), 32); + SLLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), 32); + ADDI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), 1); SetJumpTarget(keepNegOne); SetJumpTarget(skipNonZero); @@ -718,20 +716,20 @@ void RiscVJitBackend::CompIR_Div(IRInst inst) { FixupBranch notMostNegative = BNE(numReg, R_RA); LI(R_RA, -1); FixupBranch notNegativeOne = BNE(denomReg, R_RA); - LI(regs_.R(IRREG_HI), -1); + // Take our R_RA and put it in the high bits. + SLLI(R_RA, R_RA, 32); + OR(regs_.R(IRREG_LO), regs_.R(IRREG_LO), R_RA); SetJumpTarget(notNegativeOne); SetJumpTarget(notMostNegative); } - regs_.MarkGPRDirty(IRREG_LO, true); - regs_.MarkGPRDirty(IRREG_HI, true); break; case IROp::DivU: - regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 1, MIPSMap::NOINIT }, { 'G', IRREG_HI, 1, MIPSMap::NOINIT } }); + regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::NOINIT } }); // We have to do this because of the divide by zero check below. NormalizeSrc12(inst, &numReg, &denomReg, SCRATCH1, SCRATCH2, true); DIVUW(regs_.R(IRREG_LO), numReg, denomReg); - REMUW(regs_.R(IRREG_HI), numReg, denomReg); + REMUW(R_RA, numReg, denomReg); // On divide by zero, everything is correct already except the 0xFFFF case. { @@ -743,8 +741,16 @@ void RiscVJitBackend::CompIR_Div(IRInst inst) { SetJumpTarget(keepNegOne); SetJumpTarget(skipNonZero); } - regs_.MarkGPRDirty(IRREG_LO, true); - regs_.MarkGPRDirty(IRREG_HI, true); + + // Now combine the remainder in. + SLLI(R_RA, R_RA, 32); + if (cpu_info.RiscV_Zba) { + ADD_UW(regs_.R(IRREG_LO), regs_.R(IRREG_LO), R_RA); + } else { + SLLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), XLEN - 32); + SRLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), XLEN - 32); + ADD(regs_.R(IRREG_LO), regs_.R(IRREG_LO), R_RA); + } break; default: From 81e24a9fee1f25a1a5d87274b4c38ebb44ebdd76 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 22:12:12 -0700 Subject: [PATCH 15/36] irjit: Fix regalloc clobber on exit. --- Core/MIPS/IR/IRAnalysis.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/MIPS/IR/IRAnalysis.cpp b/Core/MIPS/IR/IRAnalysis.cpp index 22bdada374..e27554d712 100644 --- a/Core/MIPS/IR/IRAnalysis.cpp +++ b/Core/MIPS/IR/IRAnalysis.cpp @@ -42,9 +42,9 @@ static bool IRReadsFrom(const IRInst &inst, int reg, char type, bool *directly) if (directly) *directly = false; - if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement || inst.op == IROp::Syscall || inst.op == IROp::Break) + if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement) return true; - if (inst.op == IROp::Breakpoint || inst.op == IROp::MemoryCheck) + if ((m->flags & IRFLAG_EXIT) != 0) return true; return false; } From c491f701bab7894055283b7122df725cab7895f3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 18:19:46 -0700 Subject: [PATCH 16/36] x86jit: Add initial IR-based jit backend. It works, but pretty slow in some parts with everything stubbed. --- Core/MIPS/RiscV/RiscVJit.cpp | 8 +- Core/MIPS/x86/X64IRAsm.cpp | 264 +++++++++++++++++++++++ Core/MIPS/x86/X64IRJit.cpp | 372 ++++++++++++++++++++++++++++++++ Core/MIPS/x86/X64IRJit.h | 145 +++++++++++++ Core/MIPS/x86/X64IRRegCache.cpp | 324 ++++++++++++++++++++++++++++ Core/MIPS/x86/X64IRRegCache.h | 100 +++++++++ 6 files changed, 1208 insertions(+), 5 deletions(-) create mode 100644 Core/MIPS/x86/X64IRAsm.cpp create mode 100644 Core/MIPS/x86/X64IRJit.cpp create mode 100644 Core/MIPS/x86/X64IRJit.h create mode 100644 Core/MIPS/x86/X64IRRegCache.cpp create mode 100644 Core/MIPS/x86/X64IRRegCache.h diff --git a/Core/MIPS/RiscV/RiscVJit.cpp b/Core/MIPS/RiscV/RiscVJit.cpp index 1e9e4ef04c..d59927b8dc 100644 --- a/Core/MIPS/RiscV/RiscVJit.cpp +++ b/Core/MIPS/RiscV/RiscVJit.cpp @@ -59,7 +59,6 @@ bool RiscVJitBackend::CompileBlock(IRBlock *block, int block_num, bool preload) u32 startPC = block->GetOriginalStart(); bool wroteCheckedOffset = false; - FixupBranch lateCheckFail; if (jo.enableBlocklink && !jo.useBackJump) { SetBlockCheckedOffset(block_num, (int)GetOffset(GetCodePointer())); wroteCheckedOffset = true; @@ -130,7 +129,6 @@ bool RiscVJitBackend::CompileBlock(IRBlock *block, int block_num, bool preload) if (logBlocks_ > 0) { --logBlocks_; - INFO_LOG(JIT, "== RISCV =="); INFO_LOG(JIT, "=============== RISCV (%d bytes) ===============", len); for (const u8 *p = blockStart; p < GetCodePointer(); ) { char temp[512]; @@ -163,7 +161,7 @@ void RiscVJitBackend::WriteConstExit(uint32_t pc) { int exitStart = (int)GetOffset(GetCodePointer()); if (block_num >= 0 && jo.enableBlocklink && nativeBlock && nativeBlock->checkedOffset != 0) { - // Don't bother recording, we don't every overwrite to "unlink". + // Don't bother recording, we don't ever overwrite to "unlink". // Instead, we would mark the target block to jump to the dispatcher. QuickJ(SCRATCH1, GetBasePtr() + nativeBlock->checkedOffset); } else { @@ -283,7 +281,7 @@ void RiscVJitBackend::InvalidateBlock(IRBlock *block, int block_num) { if (pc != 0) { // Hopefully we always have at least 16 bytes, which should be all we need. if (PlatformIsWXExclusive()) { - ProtectMemoryPages(writable, 16, MEM_PROT_READ | MEM_PROT_WRITE); + ProtectMemoryPages(writable, MIN_BLOCK_NORMAL_LEN, MEM_PROT_READ | MEM_PROT_WRITE); } RiscVEmitter emitter(GetBasePtr() + offset, writable); @@ -297,7 +295,7 @@ void RiscVJitBackend::InvalidateBlock(IRBlock *block, int block_num) { emitter.FlushIcache(); if (PlatformIsWXExclusive()) { - ProtectMemoryPages(writable, 16, MEM_PROT_READ | MEM_PROT_EXEC); + ProtectMemoryPages(writable, MIN_BLOCK_NORMAL_LEN, MEM_PROT_READ | MEM_PROT_EXEC); } } diff --git a/Core/MIPS/x86/X64IRAsm.cpp b/Core/MIPS/x86/X64IRAsm.cpp new file mode 100644 index 0000000000..9019f57cb8 --- /dev/null +++ b/Core/MIPS/x86/X64IRAsm.cpp @@ -0,0 +1,264 @@ +// Copyright (c) 2023- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "ppsspp_config.h" +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + +#include "Common/Log.h" +#include "Core/CoreTiming.h" +#include "Core/MemMap.h" +#include "Core/MIPS/x86/X64IRJit.h" +#include "Core/MIPS/x86/X64IRRegCache.h" +#include "Core/MIPS/JitCommon/JitCommon.h" +#include "Core/MIPS/JitCommon/JitState.h" +#include "Core/System.h" + +namespace MIPSComp { + +using namespace Gen; +using namespace X64IRJitConstants; + +static const bool enableDebug = false; +static const bool enableDisasm = false; + +static void ShowPC(void *membase, void *jitbase) { + static int count = 0; + if (currentMIPS) { + u32 downcount = currentMIPS->downcount; + ERROR_LOG(JIT, "[%08x] ShowPC Downcount : %08x %d %p %p", currentMIPS->pc, downcount, count, membase, jitbase); + } else { + ERROR_LOG(JIT, "Universe corrupt?"); + } + //if (count > 2000) + // exit(0); + count++; +} + +void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) { + BeginWrite(GetMemoryProtectPageSize()); + const u8 *start = AlignCodePage(); + + if (jo.useStaticAlloc && false) { + saveStaticRegisters_ = AlignCode16(); + //MOV(32, MDisp(CTXREG, -128 + offsetof(MIPSState, downcount)), R(DOWNCOUNTREG)); + //regs_.EmitSaveStaticRegisters(); + RET(); + + loadStaticRegisters_ = AlignCode16(); + //regs_.EmitLoadStaticRegisters(); + //MOV(32, R(DOWNCOUNTREG), MDisp(CTXREG, -128 + offsetof(MIPSState, downcount))); + RET(); + + start = saveStaticRegisters_; + } else { + saveStaticRegisters_ = nullptr; + loadStaticRegisters_ = nullptr; + } + + restoreRoundingMode_ = AlignCode16(); + { + STMXCSR(MDisp(CTXREG, -128 + offsetof(MIPSState, temp))); + // Clear the rounding mode and flush-to-zero bits back to 0. + AND(32, MDisp(CTXREG, -128 + offsetof(MIPSState, temp)), Imm32(~(7 << 13))); + LDMXCSR(MDisp(CTXREG, -128 + offsetof(MIPSState, temp))); + RET(); + } + + applyRoundingMode_ = AlignCode16(); + { + MOV(32, R(SCRATCH1), MDisp(CTXREG, -128 + offsetof(MIPSState, fcr31))); + AND(32, R(SCRATCH1), Imm32(0x01000003)); + + // If it's 0 (nearest + no flush0), we don't actually bother setting - we cleared the rounding + // mode out in restoreRoundingMode anyway. This is the most common. + FixupBranch skip = J_CC(CC_Z); + STMXCSR(MDisp(CTXREG, -128 + offsetof(MIPSState, temp))); + + // The MIPS bits don't correspond exactly, so we have to adjust. + // 0 -> 0 (skip2), 1 -> 3, 2 -> 2 (skip2), 3 -> 1 + TEST(8, R(AL), Imm8(1)); + FixupBranch skip2 = J_CC(CC_Z); + XOR(32, R(SCRATCH1), Imm8(2)); + SetJumpTarget(skip2); + + // Adjustment complete, now reconstruct MXCSR + SHL(32, R(SCRATCH1), Imm8(13)); + // Before setting new bits, we must clear the old ones. + // Clearing bits 13-14 (rounding mode) and 15 (flush to zero.) + AND(32, MDisp(CTXREG, -128 + offsetof(MIPSState, temp)), Imm32(~(7 << 13))); + OR(32, MDisp(CTXREG, -128 + offsetof(MIPSState, temp)), R(SCRATCH1)); + + TEST(32, MDisp(CTXREG, -128 + offsetof(MIPSState, fcr31)), Imm32(1 << 24)); + FixupBranch skip3 = J_CC(CC_Z); + OR(32, MDisp(CTXREG, -128 + offsetof(MIPSState, temp)), Imm32(1 << 15)); + SetJumpTarget(skip3); + + LDMXCSR(MDisp(CTXREG, -128 + offsetof(MIPSState, temp))); + SetJumpTarget(skip); + RET(); + } + + hooks_.enterDispatcher = (IRNativeFuncNoArg)AlignCode16(); + + ABI_PushAllCalleeSavedRegsAndAdjustStack(); +#if PPSSPP_ARCH(AMD64) + // Two x64-specific statically allocated registers. + MOV(64, R(MEMBASEREG), ImmPtr(Memory::base)); + uintptr_t jitbase = (uintptr_t)GetBasePtr(); + if (jitbase > 0x7FFFFFFFULL) { + MOV(64, R(JITBASEREG), ImmPtr(GetBasePtr())); + jo.reserveR15ForAsm = true; + } +#endif + // From the start of the FP reg, a single byte offset can reach all GPR + all FPR (but not VFPR.) + MOV(PTRBITS, R(CTXREG), ImmPtr(&mipsState->f[0])); + + LoadStaticRegisters(); + MovFromPC(SCRATCH1); + outerLoopPCInSCRATCH1_ = GetCodePtr(); + MovToPC(SCRATCH1); + outerLoop_ = GetCodePtr(); + // Advance can change the downcount (or thread), so must save/restore around it. + SaveStaticRegisters(); + RestoreRoundingMode(true); + ABI_CallFunction(reinterpret_cast(&CoreTiming::Advance)); + ApplyRoundingMode(true); + LoadStaticRegisters(); + + dispatcherCheckCoreState_ = GetCodePtr(); + // TODO: See if we can get the slice decrement to line up with IR. + + if (RipAccessible((const void *)&coreState)) { + CMP(32, M(&coreState), Imm32(0)); // rip accessible + } else { + MOV(PTRBITS, R(RAX), ImmPtr((const void *)&coreState)); + CMP(32, MatR(RAX), Imm32(0)); + } + FixupBranch badCoreState = J_CC(CC_NZ, true); + + //CMP(32, R(DOWNCOUNTREG), Imm32(0)); + CMP(32, MDisp(CTXREG, -128 + offsetof(MIPSState, downcount)), Imm32(0)); + J_CC(CC_S, outerLoop_); + FixupBranch skipToRealDispatch = J(); + + dispatcherPCInSCRATCH1_ = GetCodePtr(); + MovToPC(SCRATCH1); + + hooks_.dispatcher = GetCodePtr(); + + // TODO: See if we can get the slice decrement to line up with IR. + //CMP(32, R(DOWNCOUNTREG), Imm32(0)); + CMP(32, MDisp(CTXREG, -128 + offsetof(MIPSState, downcount)), Imm32(0)); + FixupBranch bail = J_CC(CC_S, true); + SetJumpTarget(skipToRealDispatch); + + dispatcherNoCheck_ = GetCodePtr(); + + // Debug + if (enableDebug) { +#if PPSSPP_ARCH(AMD64) + ABI_CallFunctionAA(reinterpret_cast(&ShowPC), R(MEMBASEREG), R(JITBASEREG)); +#else + ABI_CallFunctionCC(reinterpret_cast(&ShowPC), Menory::base, (u32)jitbase); +#endif + } + + MovFromPC(SCRATCH1); +#ifdef MASKED_PSP_MEMORY + AND(32, R(SCRATCH1), Imm32(Memory::MEMVIEW32_MASK)); +#endif + hooks_.dispatchFetch = GetCodePtr(); +#if PPSSPP_ARCH(X86) + _assert_msg_( Memory::base != 0, "Memory base bogus"); + MOV(32, R(SCRATCH1), MDisp(SCRATCH1, (u32)Memory::base)); +#elif PPSSPP_ARCH(AMD64) + MOV(32, R(SCRATCH1), MComplex(MEMBASEREG, SCRATCH1, SCALE_1, 0)); +#endif + MOV(32, R(EDX), R(SCRATCH1)); + _assert_msg_(MIPS_JITBLOCK_MASK == 0xFF000000, "Hardcoded assumption of emuhack mask"); + SHR(32, R(EDX), Imm8(24)); + CMP(32, R(EDX), Imm8(MIPS_EMUHACK_OPCODE >> 24)); + FixupBranch needsCompile = J_CC(CC_NE); + // Mask by 0x00FFFFFF and extract the block jit offset. + AND(32, R(SCRATCH1), Imm32(MIPS_EMUHACK_VALUE_MASK)); +#if PPSSPP_ARCH(X86) + ADD(32, R(SCRATCH1), ImmPtr(GetBasePtr())); +#elif PPSSPP_ARCH(AMD64) + if (jo.reserveR15ForAsm) { + ADD(64, R(SCRATCH1), R(JITBASEREG)); + } else { + // See above, reserveR15ForAsm is used when above 0x7FFFFFFF. + ADD(64, R(SCRATCH1), Imm32((u32)jitbase)); + } +#endif + JMPptr(R(SCRATCH1)); + SetJumpTarget(needsCompile); + + // No block found, let's jit. We don't need to save static regs, they're all callee saved. + RestoreRoundingMode(true); + ABI_CallFunction(&MIPSComp::JitAt); + ApplyRoundingMode(true); + // Let's just dispatch again, we'll enter the block since we know it's there. + JMP(dispatcherNoCheck_, true); + + SetJumpTarget(bail); + + if (RipAccessible((const void *)&coreState)) { + CMP(32, M(&coreState), Imm32(0)); // rip accessible + } else { + MOV(PTRBITS, R(RAX), ImmPtr((const void *)&coreState)); + CMP(32, MatR(RAX), Imm32(0)); + } + J_CC(CC_Z, outerLoop_, true); + + const uint8_t *quitLoop = GetCodePtr(); + SetJumpTarget(badCoreState); + + SaveStaticRegisters(); + RestoreRoundingMode(true); + ABI_PopAllCalleeSavedRegsAndAdjustStack(); + RET(); + + hooks_.crashHandler = GetCodePtr(); + if (RipAccessible((const void *)&coreState)) { + MOV(32, M(&coreState), Imm32(CORE_RUNTIME_ERROR)); + } else { + MOV(PTRBITS, R(RAX), ImmPtr((const void *)&coreState)); + MOV(32, MatR(RAX), Imm32(CORE_RUNTIME_ERROR)); + } + JMP(quitLoop, true); + + + // Leave this at the end, add more stuff above. + if (enableDisasm) { +#if PPSSPP_ARCH(AMD64) + std::vector lines = DisassembleX86(start, (int)(GetCodePtr() - start)); + for (auto s : lines) { + INFO_LOG(JIT, "%s", s.c_str()); + } +#endif + } + + // Let's spare the pre-generated code from unprotect-reprotect. + AlignCodePage(); + jitStartOffset_ = (int)(GetCodePtr() - start); + EndWrite(); +} + +} // namespace MIPSComp + +#endif diff --git a/Core/MIPS/x86/X64IRJit.cpp b/Core/MIPS/x86/X64IRJit.cpp new file mode 100644 index 0000000000..7d7c90b6a0 --- /dev/null +++ b/Core/MIPS/x86/X64IRJit.cpp @@ -0,0 +1,372 @@ +// Copyright (c) 2023- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "ppsspp_config.h" +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + +#include +#include "Core/MemMap.h" +#include "Core/MIPS/MIPSTables.h" +#include "Core/MIPS/x86/X64IRJit.h" +#include "Core/MIPS/x86/X64IRRegCache.h" + +namespace MIPSComp { + +using namespace Gen; +using namespace X64IRJitConstants; + +// This should be enough for exits and invalidations. +static constexpr int MIN_BLOCK_NORMAL_LEN = 16; +static constexpr int MIN_BLOCK_EXIT_LEN = 16; + +X64JitBackend::X64JitBackend(JitOptions &jitopt, IRBlockCache &blocks) + : IRNativeBackend(blocks), jo(jitopt), regs_(&jo) { + // Automatically disable incompatible options. + if (((intptr_t)Memory::base & 0x00000000FFFFFFFFUL) != 0) { + jo.enablePointerify = false; + } + + // Since we store the offset, this is as big as it can be. + AllocCodeSpace(1024 * 1024 * 16); + + regs_.Init(this); +} + +X64JitBackend::~X64JitBackend() {} + +static void NoBlockExits() { + _assert_msg_(false, "Never exited block, invalid IR?"); +} + +bool X64JitBackend::CompileBlock(IRBlock *block, int block_num, bool preload) { + if (GetSpaceLeft() < 0x800) + return false; + + u32 startPC = block->GetOriginalStart(); + bool wroteCheckedOffset = false; + if (jo.enableBlocklink && !jo.useBackJump) { + SetBlockCheckedOffset(block_num, (int)GetOffset(GetCodePointer())); + wroteCheckedOffset = true; + + // TODO: See if we can get flags to always have the downcount compare. + //CMP(32, R(DOWNCOUNTREG), Imm32(0)); + CMP(32, MDisp(CTXREG, -128 + offsetof(MIPSState, downcount)), Imm32(0)); + FixupBranch normalEntry = J_CC(CC_NS); + MOV(32, R(SCRATCH1), Imm32(startPC)); + JMP(outerLoopPCInSCRATCH1_, true); + SetJumpTarget(normalEntry); + } + + // Don't worry, the codespace isn't large enough to overflow offsets. + const u8 *blockStart = GetCodePointer(); + block->SetTargetOffset((int)GetOffset(blockStart)); + compilingBlockNum_ = block_num; + + regs_.Start(block); + + std::map addresses; + for (int i = 0; i < block->GetNumInstructions(); ++i) { + const IRInst &inst = block->GetInstructions()[i]; + regs_.SetIRIndex(i); + // TODO: This might be a little wasteful when compiling if we're not debugging jit... + addresses[GetCodePtr()] = inst; + + CompileIRInst(inst); + + if (jo.Disabled(JitDisable::REGALLOC_GPR) || jo.Disabled(JitDisable::REGALLOC_FPR)) + regs_.FlushAll(jo.Disabled(JitDisable::REGALLOC_GPR), jo.Disabled(JitDisable::REGALLOC_FPR)); + + // Safety check, in case we get a bunch of really large jit ops without a lot of branching. + if (GetSpaceLeft() < 0x800) { + compilingBlockNum_ = -1; + return false; + } + } + + // We should've written an exit above. If we didn't, bad things will happen. + // Only check if debug stats are enabled - needlessly wastes jit space. + if (DebugStatsEnabled()) { + ABI_CallFunction((const void *)&NoBlockExits); + JMP(hooks_.crashHandler, true); + } + + int len = (int)GetOffset(GetCodePointer()) - block->GetTargetOffset(); + if (len < MIN_BLOCK_NORMAL_LEN) { + // We need at least 16 bytes to invalidate blocks with, but larger doesn't need to align. + ReserveCodeSpace(MIN_BLOCK_NORMAL_LEN - len); + } + + if (!wroteCheckedOffset) { + // Always record this, even if block link disabled - it's used for size calc. + SetBlockCheckedOffset(block_num, (int)GetOffset(GetCodePointer())); + } + + if (jo.enableBlocklink && jo.useBackJump) { + //CMP(32, R(DOWNCOUNTREG), Imm32(0)); + CMP(32, MDisp(CTXREG, -128 + offsetof(MIPSState, downcount)), Imm32(0)); + J_CC(CC_NS, blockStart, true); + + MOV(32, R(SCRATCH1), Imm32(startPC)); + JMP(outerLoopPCInSCRATCH1_, true); + } + + if (logBlocks_ > 0) { + --logBlocks_; + + INFO_LOG(JIT, "=============== x86 (%d bytes) ===============", len); + for (const u8 *p = blockStart; p < GetCodePointer(); ) { + auto it = addresses.find(p); + if (it != addresses.end()) { + + char temp[512]; + DisassembleIR(temp, sizeof(temp), it->second); + INFO_LOG(JIT, "IR: # %s", temp); + } + + auto next = std::next(it); + const u8 *nextp = next == addresses.end() ? GetCodePointer() : next->first; + + auto lines = DisassembleX86(p, (int)(nextp - p)); + for (const auto &line : lines) + INFO_LOG(JIT, " X: %s", line.c_str()); + p = nextp; + } + } + + compilingBlockNum_ = -1; + + return true; +} + +void X64JitBackend::WriteConstExit(uint32_t pc) { + int block_num = blocks_.GetBlockNumberFromStartAddress(pc); + const IRNativeBlock *nativeBlock = GetNativeBlock(block_num); + + int exitStart = (int)GetOffset(GetCodePointer()); + if (block_num >= 0 && jo.enableBlocklink && nativeBlock && nativeBlock->checkedOffset != 0) { + // Don't bother recording, we don't ever overwrite to "unlink". + // Instead, we would mark the target block to jump to the dispatcher. + JMP(GetBasePtr() + nativeBlock->checkedOffset, true); + } else { + MOV(32, R(SCRATCH1), Imm32(pc)); + JMP(dispatcherPCInSCRATCH1_, true); + } + + if (jo.enableBlocklink) { + // In case of compression or early link, make sure it's large enough. + int len = (int)GetOffset(GetCodePointer()) - exitStart; + if (len < MIN_BLOCK_EXIT_LEN) { + ReserveCodeSpace(MIN_BLOCK_EXIT_LEN - len); + len = MIN_BLOCK_EXIT_LEN; + } + + AddLinkableExit(compilingBlockNum_, pc, exitStart, len); + } +} + +void X64JitBackend::OverwriteExit(int srcOffset, int len, int block_num) { + _dbg_assert_(len >= MIN_BLOCK_EXIT_LEN); + + const IRNativeBlock *nativeBlock = GetNativeBlock(block_num); + if (nativeBlock) { + u8 *writable = GetWritablePtrFromCodePtr(GetBasePtr()) + srcOffset; + if (PlatformIsWXExclusive()) { + ProtectMemoryPages(writable, len, MEM_PROT_READ | MEM_PROT_WRITE); + } + + XEmitter emitter(writable); + emitter.JMP(GetBasePtr() + nativeBlock->checkedOffset, true); + int bytesWritten = (int)(emitter.GetWritableCodePtr() - writable); + if (bytesWritten < len) + emitter.ReserveCodeSpace(len - bytesWritten); + + if (PlatformIsWXExclusive()) { + ProtectMemoryPages(writable, 16, MEM_PROT_READ | MEM_PROT_EXEC); + } + } +} + +void X64JitBackend::CompIR_Generic(IRInst inst) { + // If we got here, we're going the slow way. + uint64_t value; + memcpy(&value, &inst, sizeof(inst)); + + FlushAll(); + SaveStaticRegisters(); +#if PPSSPP_ARCH(AMD64) + ABI_CallFunctionP((const void *)&DoIRInst, (void *)value); +#else + ABI_CallFunctionCC((const void *)&DoIRInst, (u32)(value & 0xFFFFFFFF), (u32)(value >> 32)); +#endif + LoadStaticRegisters(); + + // We only need to check the return value if it's a potential exit. + if ((GetIRMeta(inst.op)->flags & IRFLAG_EXIT) != 0) { + // Result in RAX aka SCRATCH1. + _assert_(RAX == SCRATCH1); + CMP(32, R(SCRATCH1), Imm32(0)); + J_CC(CC_NE, dispatcherPCInSCRATCH1_); + } +} + +void X64JitBackend::CompIR_Interpret(IRInst inst) { + MIPSOpcode op(inst.constant); + + // IR protects us against this being a branching instruction (well, hopefully.) + FlushAll(); + SaveStaticRegisters(); + if (DebugStatsEnabled()) { + ABI_CallFunctionP((const void *)&NotifyMIPSInterpret, (void *)MIPSGetName(op)); + } + ABI_CallFunctionC((const void *)MIPSGetInterpretFunc(op), inst.constant); + LoadStaticRegisters(); +} + +void X64JitBackend::FlushAll() { + regs_.FlushAll(); +} + +bool X64JitBackend::DescribeCodePtr(const u8 *ptr, std::string &name) const { + // Used in disassembly viewer. + if (ptr == dispatcherPCInSCRATCH1_) { + name = "dispatcher (PC in SCRATCH1)"; + } else if (ptr == dispatcherNoCheck_) { + name = "dispatcherNoCheck"; + } else if (ptr == saveStaticRegisters_) { + name = "saveStaticRegisters"; + } else if (ptr == loadStaticRegisters_) { + name = "loadStaticRegisters"; + } else if (ptr == restoreRoundingMode_) { + name = "restoreRoundingMode"; + } else if (ptr == applyRoundingMode_) { + name = "applyRoundingMode"; + } else { + return IRNativeBackend::DescribeCodePtr(ptr, name); + } + return true; +} + +void X64JitBackend::ClearAllBlocks() { + ClearCodeSpace(jitStartOffset_); + EraseAllLinks(-1); +} + +void X64JitBackend::InvalidateBlock(IRBlock *block, int block_num) { + int offset = block->GetTargetOffset(); + u8 *writable = GetWritablePtrFromCodePtr(GetBasePtr()) + offset; + + // Overwrite the block with a jump to compile it again. + u32 pc = block->GetOriginalStart(); + if (pc != 0) { + // Hopefully we always have at least 16 bytes, which should be all we need. + if (PlatformIsWXExclusive()) { + ProtectMemoryPages(writable, MIN_BLOCK_NORMAL_LEN, MEM_PROT_READ | MEM_PROT_WRITE); + } + + XEmitter emitter(writable); + emitter.MOV(32, R(SCRATCH1), Imm32(pc)); + emitter.JMP(dispatcherPCInSCRATCH1_, true); + int bytesWritten = (int)(emitter.GetWritableCodePtr() - writable); + if (bytesWritten < MIN_BLOCK_NORMAL_LEN) + emitter.ReserveCodeSpace(MIN_BLOCK_NORMAL_LEN - bytesWritten); + + if (PlatformIsWXExclusive()) { + ProtectMemoryPages(writable, MIN_BLOCK_NORMAL_LEN, MEM_PROT_READ | MEM_PROT_EXEC); + } + } + + EraseAllLinks(block_num); +} + +void X64JitBackend::RestoreRoundingMode(bool force) { + CALL(restoreRoundingMode_); +} + +void X64JitBackend::ApplyRoundingMode(bool force) { + CALL(applyRoundingMode_); +} + +void X64JitBackend::MovFromPC(X64Reg r) { + MOV(32, R(r), MDisp(CTXREG, -128 + offsetof(MIPSState, pc))); +} + +void X64JitBackend::MovToPC(X64Reg r) { + MOV(32, MDisp(CTXREG, -128 + offsetof(MIPSState, pc)), R(r)); +} + +void X64JitBackend::SaveStaticRegisters() { + if (jo.useStaticAlloc) { + //CALL(saveStaticRegisters_); + } else { + // Inline the single operation + //MOV(32, MDisp(CTXREG, -128 + offsetof(MIPSState, downcount)), R(DOWNCOUNTREG)); + } +} + +void X64JitBackend::LoadStaticRegisters() { + if (jo.useStaticAlloc) { + //CALL(loadStaticRegisters_); + } else { + //MOV(32, R(DOWNCOUNTREG), MDisp(CTXREG, -128 + offsetof(MIPSState, downcount))); + } +} + +// TODO: Move out to files. +void X64JitBackend::CompIR_Arith(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_Assign(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_Basic(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_Bits(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_Breakpoint(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_Compare(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_CondAssign(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_CondStore(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_Div(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_Exit(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_ExitIf(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_FArith(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_FAssign(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_FCompare(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_FCondAssign(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_FCvt(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_FLoad(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_FRound(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_FSat(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_FSpecial(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_FStore(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_HiLo(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_Load(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_LoadShift(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_Logic(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_Mult(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_RoundingMode(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_Shift(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_Store(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_StoreShift(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_System(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_Transfer(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_VecArith(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_VecAssign(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_VecClamp(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_VecHoriz(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_VecLoad(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_VecPack(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_VecStore(IRInst inst) { CompIR_Generic(inst); } +void X64JitBackend::CompIR_ValidateAddress(IRInst inst) { CompIR_Generic(inst); } + +} // namespace MIPSComp + +#endif diff --git a/Core/MIPS/x86/X64IRJit.h b/Core/MIPS/x86/X64IRJit.h new file mode 100644 index 0000000000..4144ece248 --- /dev/null +++ b/Core/MIPS/x86/X64IRJit.h @@ -0,0 +1,145 @@ +// Copyright (c) 2023- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "ppsspp_config.h" +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + +#include +#include +#include "Common/x64Emitter.h" +#include "Core/MIPS/IR/IRJit.h" +#include "Core/MIPS/IR/IRNativeCommon.h" +#include "Core/MIPS/JitCommon/JitState.h" +#include "Core/MIPS/JitCommon/JitCommon.h" +#include "Core/MIPS/x86/X64IRRegCache.h" + +namespace MIPSComp { + +class X64JitBackend : public Gen::XCodeBlock, public IRNativeBackend { +public: + X64JitBackend(JitOptions &jo, IRBlockCache &blocks); + ~X64JitBackend(); + + bool DescribeCodePtr(const u8 *ptr, std::string &name) const override; + + void GenerateFixedCode(MIPSState *mipsState) override; + bool CompileBlock(IRBlock *block, int block_num, bool preload) override; + void ClearAllBlocks() override; + void InvalidateBlock(IRBlock *block, int block_num) override; + +protected: + const CodeBlockCommon &CodeBlock() const override { + return *this; + } + +private: + void RestoreRoundingMode(bool force = false); + void ApplyRoundingMode(bool force = false); + void MovFromPC(Gen::X64Reg r); + void MovToPC(Gen::X64Reg r); + + void SaveStaticRegisters(); + void LoadStaticRegisters(); + + // Note: destroys SCRATCH1. + void FlushAll(); + + void WriteConstExit(uint32_t pc); + void OverwriteExit(int srcOffset, int len, int block_num) override; + + void CompIR_Arith(IRInst inst) override; + void CompIR_Assign(IRInst inst) override; + void CompIR_Basic(IRInst inst) override; + void CompIR_Bits(IRInst inst) override; + void CompIR_Breakpoint(IRInst inst) override; + void CompIR_Compare(IRInst inst) override; + void CompIR_CondAssign(IRInst inst) override; + void CompIR_CondStore(IRInst inst) override; + void CompIR_Div(IRInst inst) override; + void CompIR_Exit(IRInst inst) override; + void CompIR_ExitIf(IRInst inst) override; + void CompIR_FArith(IRInst inst) override; + void CompIR_FAssign(IRInst inst) override; + void CompIR_FCompare(IRInst inst) override; + void CompIR_FCondAssign(IRInst inst) override; + void CompIR_FCvt(IRInst inst) override; + void CompIR_FLoad(IRInst inst) override; + void CompIR_FRound(IRInst inst) override; + void CompIR_FSat(IRInst inst) override; + void CompIR_FSpecial(IRInst inst) override; + void CompIR_FStore(IRInst inst) override; + void CompIR_Generic(IRInst inst) override; + void CompIR_HiLo(IRInst inst) override; + void CompIR_Interpret(IRInst inst) override; + void CompIR_Load(IRInst inst) override; + void CompIR_LoadShift(IRInst inst) override; + void CompIR_Logic(IRInst inst) override; + void CompIR_Mult(IRInst inst) override; + void CompIR_RoundingMode(IRInst inst) override; + void CompIR_Shift(IRInst inst) override; + void CompIR_Store(IRInst inst) override; + void CompIR_StoreShift(IRInst inst) override; + void CompIR_System(IRInst inst) override; + void CompIR_Transfer(IRInst inst) override; + void CompIR_VecArith(IRInst inst) override; + void CompIR_VecAssign(IRInst inst) override; + void CompIR_VecClamp(IRInst inst) override; + void CompIR_VecHoriz(IRInst inst) override; + void CompIR_VecLoad(IRInst inst) override; + void CompIR_VecPack(IRInst inst) override; + void CompIR_VecStore(IRInst inst) override; + void CompIR_ValidateAddress(IRInst inst) override; + + void SetScratch1ToSrc1Address(IRReg src1); + // Modifies SCRATCH regs. + int32_t AdjustForAddressOffset(Gen::X64Reg *reg, int32_t constant, int32_t range = 0); + + JitOptions &jo; + X64IRRegCache regs_; + + const u8 *outerLoop_ = nullptr; + const u8 *outerLoopPCInSCRATCH1_ = nullptr; + const u8 *dispatcherCheckCoreState_ = nullptr; + const u8 *dispatcherPCInSCRATCH1_ = nullptr; + const u8 *dispatcherNoCheck_ = nullptr; + const u8 *restoreRoundingMode_ = nullptr; + const u8 *applyRoundingMode_ = nullptr; + + const u8 *saveStaticRegisters_ = nullptr; + const u8 *loadStaticRegisters_ = nullptr; + + int jitStartOffset_ = 0; + int compilingBlockNum_ = -1; + int logBlocks_ = 0; +}; + +class X64IRJit : public IRNativeJit { +public: + X64IRJit(MIPSState *mipsState) + : IRNativeJit(mipsState), rvBackend_(jo, blocks_) { + Init(rvBackend_); + } + +private: + X64JitBackend rvBackend_; +}; + +} // namespace MIPSComp + +#endif diff --git a/Core/MIPS/x86/X64IRRegCache.cpp b/Core/MIPS/x86/X64IRRegCache.cpp new file mode 100644 index 0000000000..4deae084b4 --- /dev/null +++ b/Core/MIPS/x86/X64IRRegCache.cpp @@ -0,0 +1,324 @@ +// Copyright (c) 2023- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "ppsspp_config.h" +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + +#ifndef offsetof +#include +#endif + +#include "Common/CPUDetect.h" +#include "Core/MemMap.h" +#include "Core/MIPS/IR/IRInst.h" +#include "Core/MIPS/IR/IRAnalysis.h" +#include "Core/MIPS/x86/X64IRRegCache.h" +#include "Core/MIPS/JitCommon/JitState.h" +#include "Core/Reporting.h" + +using namespace Gen; +using namespace X64IRJitConstants; + +X64IRRegCache::X64IRRegCache(MIPSComp::JitOptions *jo) + : IRNativeRegCacheBase(jo) { + // TODO: Enable SIMD. + config_.totalNativeRegs = NUM_X_REGS + NUM_X_FREGS; + config_.mapFPUSIMD = false; + // XMM regs are used for both FPU and Vec, so we don't need VREGs. + config_.mapUseVRegs = false; +} + +void X64IRRegCache::Init(XEmitter *emitter) { + emit_ = emitter; +} + +const int *X64IRRegCache::GetAllocationOrder(MIPSLoc type, int &count, int &base) const { + if (type == MIPSLoc::REG) { + base = RAX; + + static const int allocationOrder[] = { + // On x64, RCX and RDX are the first args. CallProtectedFunction() assumes they're not regcached. +#if PPSSPP_ARCH(AMD64) +#ifdef _WIN32 + RSI, RDI, R8, R9, R10, R11, R12, R13, +#else + RBP, R8, R9, R10, R11, R12, R13, +#endif +#elif PPSSPP_ARCH(X86) + ESI, EDI, EDX, ECX, EBX, +#endif + // Intentionally last. + R15, + }; + + if (!jo_->reserveR15ForAsm) + count = ARRAY_SIZE(allocationOrder) - 1; + else + count = ARRAY_SIZE(allocationOrder); + return allocationOrder; + } else if (type == MIPSLoc::FREG) { + base = -NUM_X_REGS; + + // TODO: Might have to change this if we can't live without dedicated temps. + static const int allocationOrder[] = { +#if PPSSPP_ARCH(AMD64) + XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, XMM0, XMM1, XMM2, XMM3, XMM4, XMM5 +#elif PPSSPP_ARCH(X86) + XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, +#endif + }; + + count = ARRAY_SIZE(allocationOrder); + return allocationOrder; + } else { + _assert_msg_(false, "Allocation order not yet implemented"); + count = 0; + return nullptr; + } +} + +void X64IRRegCache::FlushBeforeCall() { + // These registers are not preserved by function calls. +#if PPSSPP_ARCH(AMD64) +#ifdef _WIN32 + FlushNativeReg(GPRToNativeReg(RCX)); + FlushNativeReg(GPRToNativeReg(RDX)); + FlushNativeReg(GPRToNativeReg(R8)); + FlushNativeReg(GPRToNativeReg(R9)); + FlushNativeReg(GPRToNativeReg(R10)); + FlushNativeReg(GPRToNativeReg(R11)); + for (int i = 0; i < 6; ++i) + FlushNativeReg(NUM_X_REGS + i); +#else + FlushNativeReg(GPRToNativeReg(R8)); + FlushNativeReg(GPRToNativeReg(R9)); + FlushNativeReg(GPRToNativeReg(R10)); + FlushNativeReg(GPRToNativeReg(R11)); + for (int i = 0; i < NUM_X_FREGS; ++i) + FlushNativeReg(NUM_X_REGS + i); +#endif +#elif PPSSPP_ARCH(X86) + FlushNativeReg(GPRToNativeReg(ECX)); + FlushNativeReg(GPRToNativeReg(EDX)); + for (int i = 0; i < NUM_X_FREGS; ++i) + FlushNativeReg(NUM_X_REGS + i); +#endif +} + +X64Reg X64IRRegCache::TryMapTempImm(IRReg r) { + _dbg_assert_(IsValidGPR(r)); + // If already mapped, no need for a temporary. + if (IsGPRMapped(r)) { + return R(r); + } + + if (mr[r].loc == MIPSLoc::IMM) { + // Try our luck - check for an exact match in another xreg. + for (int i = 0; i < TOTAL_MAPPABLE_IRREGS; ++i) { + if (mr[i].loc == MIPSLoc::REG_IMM && mr[i].imm == mr[r].imm) { + // Awesome, let's just use this reg. + return FromNativeReg(mr[i].nReg); + } + } + } + + return INVALID_REG; +} + +X64Reg X64IRRegCache::GetAndLockTempR() { + X64Reg reg = FromNativeReg(AllocateReg(MIPSLoc::REG)); + if (reg != INVALID_REG) { + nr[reg].tempLockIRIndex = irIndex_; + } + return reg; +} + +X64Reg X64IRRegCache::MapWithFPRTemp(IRInst &inst) { + return FromNativeReg(MapWithTemp(inst, MIPSLoc::FREG)); +} + +X64Reg X64IRRegCache::MapGPR(IRReg mipsReg, MIPSMap mapFlags) { + _dbg_assert_(IsValidGPR(mipsReg)); + + // Okay, not mapped, so we need to allocate an RV register. + IRNativeReg nreg = MapNativeReg(MIPSLoc::REG, mipsReg, 1, mapFlags); + return FromNativeReg(nreg); +} + +X64Reg X64IRRegCache::MapGPRAsPointer(IRReg reg) { + return FromNativeReg(MapNativeRegAsPointer(reg)); +} + +X64Reg X64IRRegCache::MapFPR(IRReg mipsReg, MIPSMap mapFlags) { + _dbg_assert_(IsValidFPR(mipsReg)); + _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::MEM || mr[mipsReg + 32].loc == MIPSLoc::FREG); + + IRNativeReg nreg = MapNativeReg(MIPSLoc::FREG, mipsReg + 32, 1, mapFlags); + if (nreg != -1) + return FromNativeReg(nreg); + return INVALID_REG; +} + +void X64IRRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { + _assert_(nreg >= 0 && nreg < NUM_X_REGS); + X64Reg r = FromNativeReg(nreg); + if (state) { +#if defined(MASKED_PSP_MEMORY) + // This destroys the value... + _dbg_assert_(!nr[nreg].isDirty); + emit_->AND(PTRBITS, ::R(r), Imm32(Memory::MEMVIEW32_MASK)); + emit_->ADD(PTRBITS, ::R(r), ImmPtr(Memory::base)); +#else + // Clear the top bits to be safe. + emit_->ADD(PTRBITS, ::R(r), ::R(MEMBASEREG)); +#endif + } else { +#if defined(MASKED_PSP_MEMORY) + _dbg_assert_(!nr[nreg].isDirty); + emit_->SUB(PTRBITS, ::R(r), ImmPtr(Memory::base)); +#else + emit_->SUB(PTRBITS, ::R(r), ::R(MEMBASEREG)); +#endif + } +} + +void X64IRRegCache::LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) { + X64Reg r = FromNativeReg(nreg); + _dbg_assert_(first != MIPS_REG_ZERO); + if (nreg < NUM_X_REGS) { + // Multilane not yet supported. + _assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO)); + if (lanes == 1) + emit_->MOV(32, ::R(r), MDisp(CTXREG, -128 + GetMipsRegOffset(first))); +#if PPSSPP_ARCH(AMD64) + else if (lanes == 2) + emit_->MOV(64, ::R(r), MDisp(CTXREG, -128 + GetMipsRegOffset(first))); +#endif + else + _assert_(false); + } else { + _dbg_assert_(nreg < NUM_X_REGS + NUM_X_FREGS); + _assert_msg_(mr[first].loc == MIPSLoc::FREG, "Cannot load this type: %d", (int)mr[first].loc); + if (lanes == 1) + emit_->MOVSS(r, MDisp(CTXREG, -128 + GetMipsRegOffset(first))); + else if (lanes == 2) + emit_->MOVLPS(r, MDisp(CTXREG, -128 + GetMipsRegOffset(first))); + else if (lanes == 4) + emit_->MOVUPS(r, MDisp(CTXREG, -128 + GetMipsRegOffset(first))); + else + _assert_(false); + } +} + +void X64IRRegCache::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) { + X64Reg r = FromNativeReg(nreg); + _dbg_assert_(first != MIPS_REG_ZERO); + if (nreg < NUM_X_REGS) { + // Multilane not yet supported. + _assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO)); + _assert_(mr[first].loc == MIPSLoc::REG || mr[first].loc == MIPSLoc::REG_IMM); + if (lanes == 1) + emit_->MOV(32, MDisp(CTXREG, -128 + GetMipsRegOffset(first)), ::R(r)); +#if PPSSPP_ARCH(AMD64) + else if (lanes == 2) + emit_->MOV(64, MDisp(CTXREG, -128 + GetMipsRegOffset(first)), ::R(r)); +#endif + else + _assert_(false); + } else { + _dbg_assert_(nreg < NUM_X_REGS + NUM_X_FREGS); + _assert_msg_(mr[first].loc == MIPSLoc::FREG, "Cannot store this type: %d", (int)mr[first].loc); + if (lanes == 1) + emit_->MOVSS(MDisp(CTXREG, -128 + GetMipsRegOffset(first)), r); + else if (lanes == 2) + emit_->MOVLPS(MDisp(CTXREG, -128 + GetMipsRegOffset(first)), r); + else if (lanes == 4) + emit_->MOVUPS(MDisp(CTXREG, -128 + GetMipsRegOffset(first)), r); + else + _assert_(false); + } +} + +void X64IRRegCache::SetNativeRegValue(IRNativeReg nreg, uint32_t imm) { + X64Reg r = FromNativeReg(nreg); + _dbg_assert_(nreg >= 0 && nreg < NUM_X_REGS); + emit_->MOV(32, ::R(r), Imm32(imm)); +} + +void X64IRRegCache::StoreRegValue(IRReg mreg, uint32_t imm) { + _assert_(IsValidGPRNoZero(mreg)); + // Try to optimize using a different reg. + X64Reg storeReg = INVALID_REG; + + // Could we get lucky? Check for an exact match in another xreg. + for (int i = 0; i < TOTAL_MAPPABLE_IRREGS; ++i) { + if (mr[i].loc == MIPSLoc::REG_IMM && mr[i].imm == imm) { + // Awesome, let's just store this reg. + storeReg = (X64Reg)mr[i].nReg; + break; + } + } + + if (storeReg == INVALID_REG) + emit_->MOV(32, MDisp(CTXREG, -128 + GetMipsRegOffset(mreg)), Imm32(imm)); + else + emit_->MOV(32, MDisp(CTXREG, -128 + GetMipsRegOffset(mreg)), ::R(storeReg)); +} + +X64Reg X64IRRegCache::R(IRReg mipsReg) { + _dbg_assert_(IsValidGPR(mipsReg)); + _dbg_assert_(mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM); + if (mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM) { + return FromNativeReg(mr[mipsReg].nReg); + } else { + ERROR_LOG_REPORT(JIT, "Reg %i not in riscv reg", mipsReg); + return INVALID_REG; // BAAAD + } +} + +X64Reg X64IRRegCache::RPtr(IRReg mipsReg) { + _dbg_assert_(IsValidGPR(mipsReg)); + _dbg_assert_(mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM || mr[mipsReg].loc == MIPSLoc::REG_AS_PTR); + if (mr[mipsReg].loc == MIPSLoc::REG_AS_PTR) { + return FromNativeReg(mr[mipsReg].nReg); + } else if (mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM) { + int rv = mr[mipsReg].nReg; + _dbg_assert_(nr[rv].pointerified); + if (nr[rv].pointerified) { + return FromNativeReg(mr[mipsReg].nReg); + } else { + ERROR_LOG(JIT, "Tried to use a non-pointer register as a pointer"); + return INVALID_REG; + } + } else { + ERROR_LOG_REPORT(JIT, "Reg %i not in x64 reg", mipsReg); + return INVALID_REG; // BAAAD + } +} + +X64Reg X64IRRegCache::F(IRReg mipsReg) { + _dbg_assert_(IsValidFPR(mipsReg)); + _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::FREG); + if (mr[mipsReg + 32].loc == MIPSLoc::FREG) { + return FromNativeReg(mr[mipsReg + 32].nReg); + } else { + ERROR_LOG_REPORT(JIT, "Reg %i not in x64 reg", mipsReg); + return INVALID_REG; // BAAAD + } +} + +#endif diff --git a/Core/MIPS/x86/X64IRRegCache.h b/Core/MIPS/x86/X64IRRegCache.h new file mode 100644 index 0000000000..5054caeaf3 --- /dev/null +++ b/Core/MIPS/x86/X64IRRegCache.h @@ -0,0 +1,100 @@ +// Copyright (c) 2023- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "ppsspp_config.h" +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + +#include "Common/x64Emitter.h" +#include "Core/MIPS/MIPS.h" +#include "Core/MIPS/IR/IRJit.h" +#include "Core/MIPS/IR/IRRegCache.h" + +namespace X64IRJitConstants { + +#if PPSSPP_ARCH(AMD64) +const Gen::X64Reg MEMBASEREG = Gen::RBX; +const Gen::X64Reg CTXREG = Gen::R14; +const Gen::X64Reg JITBASEREG = Gen::R15; +#else +const Gen::X64Reg CTXREG = Gen::EBP; +#endif +const Gen::X64Reg SCRATCH1 = Gen::EAX; + +} // namespace X64IRJitConstants + +class X64IRRegCache : public IRNativeRegCacheBase { +public: + X64IRRegCache(MIPSComp::JitOptions *jo); + + void Init(Gen::XEmitter *emitter); + + // May fail and return INVALID_REG if it needs flushing. + Gen::X64Reg TryMapTempImm(IRReg); + + // Returns an RV register containing the requested MIPS register. + Gen::X64Reg MapGPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); + Gen::X64Reg MapGPRAsPointer(IRReg reg); + Gen::X64Reg MapFPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); + + Gen::X64Reg MapWithFPRTemp(IRInst &inst); + + void FlushBeforeCall(); + + Gen::X64Reg GetAndLockTempR(); + + Gen::X64Reg R(IRReg preg); // Returns a cached register, while checking that it's NOT mapped as a pointer + Gen::X64Reg RPtr(IRReg preg); // Returns a cached register, if it has been mapped as a pointer + Gen::X64Reg F(IRReg preg); + +protected: + const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const override; + void AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) override; + + void LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; + void StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; + void SetNativeRegValue(IRNativeReg nreg, uint32_t imm) override; + void StoreRegValue(IRReg mreg, uint32_t imm) override; + +private: + IRNativeReg GPRToNativeReg(Gen::X64Reg r) { + return (IRNativeReg)r; + } + IRNativeReg XMMToNativeReg(Gen::X64Reg r) { + return (IRNativeReg)(r + NUM_X_REGS); + } + Gen::X64Reg FromNativeReg(IRNativeReg r) { + if (r >= NUM_X_REGS) + return (Gen::X64Reg)(Gen::XMM0 + (r - NUM_X_REGS)); + return (Gen::X64Reg)(Gen::RAX + r); + } + + Gen::XEmitter *emit_ = nullptr; + + enum { +#if PPSSPP_ARCH(AMD64) + NUM_X_REGS = 16, + NUM_X_FREGS = 16, +#elif PPSSPP_ARCH(X86) + NUM_X_REGS = 8, + NUM_X_FREGS = 8, +#endif + }; +}; + +#endif From 622c69dbb9e1c0900042d82f5130cab710bd6c56 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 18:36:06 -0700 Subject: [PATCH 17/36] x86jit: Expose option to select new IR based jit. --- CMakeLists.txt | 5 +++++ Core/Config.cpp | 14 +++++++------- Core/ConfigValues.h | 3 ++- Core/Core.cpp | 4 ++-- Core/Core.vcxproj | 5 +++++ Core/Core.vcxproj.filters | 15 +++++++++++++++ Core/MIPS/JitCommon/JitCommon.cpp | 5 ++++- Core/MIPS/JitCommon/JitCommon.h | 2 +- Core/MIPS/MIPS.cpp | 14 ++++++++------ GPU/Common/DrawEngineCommon.cpp | 2 +- GPU/Common/VertexDecoderCommon.cpp | 2 +- UI/EmuScreen.cpp | 1 + UI/GameSettingsScreen.cpp | 7 ++++++- UI/NativeApp.cpp | 6 +++++- UWP/CoreUWP/CoreUWP.vcxproj | 5 +++++ UWP/CoreUWP/CoreUWP.vcxproj.filters | 15 +++++++++++++++ android/jni/Android.mk | 3 +++ headless/Headless.cpp | 4 +++- libretro/Makefile.common | 3 +++ libretro/libretro.cpp | 4 ++-- 20 files changed, 94 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ae082f168..db9ae9f230 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1611,6 +1611,11 @@ list(APPEND CoreExtra Core/MIPS/x86/RegCache.h Core/MIPS/x86/RegCacheFPU.cpp Core/MIPS/x86/RegCacheFPU.h + Core/MIPS/x86/X64IRAsm.cpp + Core/MIPS/x86/X64IRJit.cpp + Core/MIPS/x86/X64IRJit.h + Core/MIPS/x86/X64IRRegCache.cpp + Core/MIPS/x86/X64IRRegCache.h GPU/Common/VertexDecoderX86.cpp GPU/Software/DrawPixelX86.cpp GPU/Software/SamplerX86.cpp diff --git a/Core/Config.cpp b/Core/Config.cpp index fca0b708ad..3873d026b1 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -133,9 +133,9 @@ static int DefaultCpuCore() { #if PPSSPP_ARCH(ARM) || PPSSPP_ARCH(ARM64) || PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(RISCV64) if (System_GetPropertyBool(SYSPROP_CAN_JIT)) return (int)CPUCore::JIT; - return (int)CPUCore::IR_JIT; + return (int)CPUCore::IR_INTERPRETER; #else - return (int)CPUCore::IR_JIT; + return (int)CPUCore::IR_INTERPRETER; #endif } @@ -1313,9 +1313,9 @@ bool Config::Save(const char *saveReason) { void Config::PostLoadCleanup(bool gameSpecific) { // Override ppsspp.ini JIT value to prevent crashing - jitForcedOff = DefaultCpuCore() != (int)CPUCore::JIT && g_Config.iCpuCore == (int)CPUCore::JIT; + jitForcedOff = DefaultCpuCore() != (int)CPUCore::JIT && (g_Config.iCpuCore == (int)CPUCore::JIT || g_Config.iCpuCore == (int)CPUCore::JIT_IR); if (jitForcedOff) { - g_Config.iCpuCore = (int)CPUCore::IR_JIT; + g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER; } // This caps the exponent 4 (so 16x.) @@ -1345,7 +1345,7 @@ void Config::PostLoadCleanup(bool gameSpecific) { void Config::PreSaveCleanup(bool gameSpecific) { if (jitForcedOff) { // If we forced jit off and it's still set to IR, change it back to jit. - if (g_Config.iCpuCore == (int)CPUCore::IR_JIT) + if (g_Config.iCpuCore == (int)CPUCore::IR_INTERPRETER) g_Config.iCpuCore = (int)CPUCore::JIT; } } @@ -1354,12 +1354,12 @@ void Config::PostSaveCleanup(bool gameSpecific) { if (jitForcedOff) { // Force JIT off again just in case Config::Save() is called without exiting PPSSPP. if (g_Config.iCpuCore == (int)CPUCore::JIT) - g_Config.iCpuCore = (int)CPUCore::IR_JIT; + g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER; } } void Config::NotifyUpdatedCpuCore() { - if (jitForcedOff && g_Config.iCpuCore == (int)CPUCore::IR_JIT) { + if (jitForcedOff && g_Config.iCpuCore == (int)CPUCore::IR_INTERPRETER) { // No longer forced off, the user set it to IR jit. jitForcedOff = false; } diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h index 9765846e64..e7a6957e5c 100644 --- a/Core/ConfigValues.h +++ b/Core/ConfigValues.h @@ -50,7 +50,8 @@ struct ConfigCustomButton { enum class CPUCore { INTERPRETER = 0, JIT = 1, - IR_JIT = 2, + IR_INTERPRETER = 2, + JIT_IR = 3, }; enum { diff --git a/Core/Core.cpp b/Core/Core.cpp index 6dc3fd759a..b9293259a9 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -411,7 +411,7 @@ const char *ExecExceptionTypeAsString(ExecExceptionType type) { void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type) { const char *desc = MemoryExceptionTypeAsString(type); // In jit, we only flush PC when bIgnoreBadMemAccess is off. - if (g_Config.iCpuCore == (int)CPUCore::JIT && g_Config.bIgnoreBadMemAccess) { + if ((g_Config.iCpuCore == (int)CPUCore::JIT || g_Config.iCpuCore == (int)CPUCore::JIT_IR) && g_Config.bIgnoreBadMemAccess) { WARN_LOG(MEMMAP, "%s: Invalid access at %08x (size %08x)", desc, address, accessSize); } else { WARN_LOG(MEMMAP, "%s: Invalid access at %08x (size %08x) PC %08x LR %08x", desc, address, accessSize, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]); @@ -439,7 +439,7 @@ void Core_MemoryException(u32 address, u32 accessSize, u32 pc, MemoryExceptionTy void Core_MemoryExceptionInfo(u32 address, u32 accessSize, u32 pc, MemoryExceptionType type, std::string additionalInfo, bool forceReport) { const char *desc = MemoryExceptionTypeAsString(type); // In jit, we only flush PC when bIgnoreBadMemAccess is off. - if (g_Config.iCpuCore == (int)CPUCore::JIT && g_Config.bIgnoreBadMemAccess) { + if ((g_Config.iCpuCore == (int)CPUCore::JIT || g_Config.iCpuCore == (int)CPUCore::JIT_IR) && g_Config.bIgnoreBadMemAccess) { WARN_LOG(MEMMAP, "%s: Invalid access at %08x (size %08x). %s", desc, address, accessSize, additionalInfo.c_str()); } else { WARN_LOG(MEMMAP, "%s: Invalid access at %08x (size %08x) PC %08x LR %08x %s", desc, address, accessSize, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA], additionalInfo.c_str()); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 51ad7941a0..e011e967e8 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -605,6 +605,8 @@ + + @@ -1091,6 +1093,7 @@ + @@ -1178,6 +1181,7 @@ + @@ -1429,6 +1433,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 190a475991..27da24f9b4 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1243,6 +1243,15 @@ Core + + MIPS\x86 + + + MIPS\x86 + + + MIPS\x86 + @@ -2004,6 +2013,12 @@ Core + + MIPS\x86 + + + MIPS\x86 + diff --git a/Core/MIPS/JitCommon/JitCommon.cpp b/Core/MIPS/JitCommon/JitCommon.cpp index 1adfda298a..48feebbbb5 100644 --- a/Core/MIPS/JitCommon/JitCommon.cpp +++ b/Core/MIPS/JitCommon/JitCommon.cpp @@ -43,6 +43,7 @@ #include "../ARM64/Arm64Jit.h" #elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) #include "../x86/Jit.h" +#include "../x86/X64IRJit.h" #elif PPSSPP_ARCH(MIPS) #include "../MIPS/MipsJit.h" #elif PPSSPP_ARCH(RISCV64) @@ -101,12 +102,14 @@ namespace MIPSComp { return notTakenTarget; } - JitInterface *CreateNativeJit(MIPSState *mipsState) { + JitInterface *CreateNativeJit(MIPSState *mipsState, bool useIR) { #if PPSSPP_ARCH(ARM) return new MIPSComp::ArmJit(mipsState); #elif PPSSPP_ARCH(ARM64) return new MIPSComp::Arm64Jit(mipsState); #elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + if (useIR) + return new MIPSComp::X64IRJit(mipsState); return new MIPSComp::Jit(mipsState); #elif PPSSPP_ARCH(MIPS) return new MIPSComp::MipsJit(mipsState); diff --git a/Core/MIPS/JitCommon/JitCommon.h b/Core/MIPS/JitCommon/JitCommon.h index c88c9fe345..2698e026ec 100644 --- a/Core/MIPS/JitCommon/JitCommon.h +++ b/Core/MIPS/JitCommon/JitCommon.h @@ -177,5 +177,5 @@ namespace MIPSComp { void DoDummyJitState(PointerWrap &p); - JitInterface *CreateNativeJit(MIPSState *mipsState); + JitInterface *CreateNativeJit(MIPSState *mipsState, bool useIR); } diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp index d4393a338a..f76b6c3b2b 100644 --- a/Core/MIPS/MIPS.cpp +++ b/Core/MIPS/MIPS.cpp @@ -211,9 +211,9 @@ void MIPSState::Init() { downcount = 0; std::lock_guard guard(MIPSComp::jitLock); - if (PSP_CoreParameter().cpuCore == CPUCore::JIT) { - MIPSComp::jit = MIPSComp::CreateNativeJit(this); - } else if (PSP_CoreParameter().cpuCore == CPUCore::IR_JIT) { + if (PSP_CoreParameter().cpuCore == CPUCore::JIT || PSP_CoreParameter().cpuCore == CPUCore::JIT_IR) { + MIPSComp::jit = MIPSComp::CreateNativeJit(this, PSP_CoreParameter().cpuCore == CPUCore::JIT_IR); + } else if (PSP_CoreParameter().cpuCore == CPUCore::IR_INTERPRETER) { MIPSComp::jit = new MIPSComp::IRJit(this); } else { MIPSComp::jit = nullptr; @@ -235,16 +235,17 @@ void MIPSState::UpdateCore(CPUCore desired) { switch (PSP_CoreParameter().cpuCore) { case CPUCore::JIT: + case CPUCore::JIT_IR: INFO_LOG(CPU, "Switching to JIT"); if (oldjit) { std::lock_guard guard(MIPSComp::jitLock); MIPSComp::jit = nullptr; delete oldjit; } - newjit = MIPSComp::CreateNativeJit(this); + newjit = MIPSComp::CreateNativeJit(this, PSP_CoreParameter().cpuCore == CPUCore::JIT_IR); break; - case CPUCore::IR_JIT: + case CPUCore::IR_INTERPRETER: INFO_LOG(CPU, "Switching to IRJIT"); if (oldjit) { std::lock_guard guard(MIPSComp::jitLock); @@ -332,7 +333,8 @@ void MIPSState::SingleStep() { int MIPSState::RunLoopUntil(u64 globalTicks) { switch (PSP_CoreParameter().cpuCore) { case CPUCore::JIT: - case CPUCore::IR_JIT: + case CPUCore::JIT_IR: + case CPUCore::IR_INTERPRETER: while (inDelaySlot) { // We must get out of the delay slot before going into jit. SingleStep(); diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index 5c0858791c..71439d028c 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -36,7 +36,7 @@ enum { }; DrawEngineCommon::DrawEngineCommon() : decoderMap_(16) { - if (g_Config.bVertexDecoderJit && g_Config.iCpuCore == (int)CPUCore::JIT) { + if (g_Config.bVertexDecoderJit && (g_Config.iCpuCore == (int)CPUCore::JIT || g_Config.iCpuCore == (int)CPUCore::JIT_IR)) { decJitCache_ = new VertexDecoderJitCache(); } transformed_ = (TransformedVertex *)AllocateMemoryPages(TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 28eb46fcdc..d6e37cf0ad 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -1524,7 +1524,7 @@ VertexDecoderJitCache::VertexDecoderJitCache() } void VertexDecoderJitCache::Clear() { - if (g_Config.iCpuCore == (int)CPUCore::JIT) { + if (g_Config.iCpuCore == (int)CPUCore::JIT || g_Config.iCpuCore == (int)CPUCore::JIT_IR) { ClearCodeSpace(0); } } diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 156ba94478..4ecbb02402 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1177,6 +1177,7 @@ static const char *CPUCoreAsString(int core) { case 0: return "Interpreter"; case 1: return "JIT"; case 2: return "IR Interpreter"; + case 3: return "JIT Using IR"; default: return "N/A"; } } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 110c74f070..06f0047f07 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1663,7 +1663,7 @@ void DeveloperToolsScreen::CreateViews() { // iOS can now use JIT on all modes, apparently. // The bool may come in handy for future non-jit platforms though (UWP XB1?) - static const char *cpuCores[] = {"Interpreter", "Dynarec (JIT)", "IR Interpreter"}; + static const char *cpuCores[] = {"Interpreter", "Dynarec (JIT)", "IR Interpreter", "JIT Using IR"}; PopupMultiChoice *core = list->Add(new PopupMultiChoice(&g_Config.iCpuCore, gr->T("CPU Core"), cpuCores, 0, ARRAY_SIZE(cpuCores), I18NCat::SYSTEM, screenManager())); core->OnChoice.Handle(this, &DeveloperToolsScreen::OnJitAffectingSetting); core->OnChoice.Add([](UI::EventParams &) { @@ -1672,7 +1672,12 @@ void DeveloperToolsScreen::CreateViews() { }); if (!canUseJit) { core->HideChoice(1); + core->HideChoice(3); } + // TODO: Enable on more architectures. +#if !PPSSPP_ARCH(X86) && !PPSSPP_ARCH(AMD64) + core->HideChoice(3); +#endif list->Add(new Choice(dev->T("JIT debug tools")))->OnClick.Handle(this, &DeveloperToolsScreen::OnJitDebugTools); list->Add(new CheckBox(&g_Config.bShowDeveloperMenu, dev->T("Show Developer Menu"))); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 7f544bdd1b..9d5dfb802c 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -603,7 +603,11 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch g_Config.bSaveSettings = false; break; case 'r': - g_Config.iCpuCore = (int)CPUCore::IR_JIT; + g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER; + g_Config.bSaveSettings = false; + break; + case 'J': + g_Config.iCpuCore = (int)CPUCore::JIT_IR; g_Config.bSaveSettings = false; break; case '-': diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 4f669a1323..a2914b2615 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -304,6 +304,8 @@ + + @@ -573,6 +575,9 @@ + + + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index 3d49ff54fc..58215c1e3d 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -189,6 +189,15 @@ MIPS\x86 + + MIPS\x86 + + + MIPS\x86 + + + MIPS\x86 + FileSystems @@ -1200,6 +1209,12 @@ MIPS\x86 + + MIPS\x86 + + + MIPS\x86 + FileSystems diff --git a/android/jni/Android.mk b/android/jni/Android.mk index b98569a2d5..371872c544 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -303,6 +303,9 @@ ARCH_FILES := \ $(SRC)/Core/MIPS/x86/JitSafeMem.cpp \ $(SRC)/Core/MIPS/x86/RegCache.cpp \ $(SRC)/Core/MIPS/x86/RegCacheFPU.cpp \ + $(SRC)/Core/MIPS/x86/X64IRAsm.cpp \ + $(SRC)/Core/MIPS/x86/X64IRJit.cpp \ + $(SRC)/Core/MIPS/x86/X64IRRegCache.cpp \ $(SRC)/GPU/Common/VertexDecoderX86.cpp \ $(SRC)/GPU/Software/DrawPixelX86.cpp \ $(SRC)/GPU/Software/SamplerX86.cpp diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 79aeed0eb5..9ebe83d2b0 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -365,8 +365,10 @@ int main(int argc, const char* argv[]) cpuCore = CPUCore::INTERPRETER; else if (!strcmp(argv[i], "-j")) cpuCore = CPUCore::JIT; + else if (!strcmp(argv[i], "--jit-ir")) + cpuCore = CPUCore::JIT_IR; else if (!strcmp(argv[i], "--ir")) - cpuCore = CPUCore::IR_JIT; + cpuCore = CPUCore::IR_INTERPRETER; else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compare")) testOptions.compare = true; else if (!strcmp(argv[i], "--bench")) diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 373af9fe31..020c2652d2 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -802,6 +802,9 @@ ifeq ($(WITH_DYNAREC),1) $(COREDIR)/MIPS/x86/JitSafeMem.cpp \ $(COREDIR)/MIPS/x86/RegCache.cpp \ $(COREDIR)/MIPS/x86/RegCacheFPU.cpp \ + $(COREDIR)/MIPS/x86/X64IRAsm.cpp \ + $(COREDIR)/MIPS/x86/X64IRJit.cpp \ + $(COREDIR)/MIPS/x86/X64IRRegCache.cpp \ $(GPUDIR)/Common/VertexDecoderX86.cpp endif endif diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 2a35bf2453..5f0764930b 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -481,7 +481,7 @@ static void check_variables(CoreParameter &coreParam) if (!strcmp(var.value, "JIT")) g_Config.iCpuCore = (int)CPUCore::JIT; else if (!strcmp(var.value, "IR JIT")) - g_Config.iCpuCore = (int)CPUCore::IR_JIT; + g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER; else if (!strcmp(var.value, "Interpreter")) g_Config.iCpuCore = (int)CPUCore::INTERPRETER; } @@ -490,7 +490,7 @@ static void check_variables(CoreParameter &coreParam) // Just gonna force it to the IR interpreter on startup. // We don't hide the option, but we make sure it's off on bootup. In case someone wants // to experiment in future iOS versions or something... - g_Config.iCpuCore = (int)CPUCore::IR_JIT; + g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER; } #else g_Config.iCpuCore = (int)CPUCore::INTERPRETER; From 4e3f3860f9dc447eec14db9560c32b52c339d602 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 19:15:44 -0700 Subject: [PATCH 18/36] x86jit: Stub out op categories to files. --- CMakeLists.txt | 6 + Core/Core.vcxproj | 6 + Core/Core.vcxproj.filters | 18 +++ Core/MIPS/RiscV/RiscVCompBranch.cpp | 1 - Core/MIPS/RiscV/RiscVCompFPU.cpp | 1 - Core/MIPS/x86/X64IRCompALU.cpp | 220 +++++++++++++++++++++++++++ Core/MIPS/x86/X64IRCompBranch.cpp | 82 ++++++++++ Core/MIPS/x86/X64IRCompFPU.cpp | 199 ++++++++++++++++++++++++ Core/MIPS/x86/X64IRCompLoadStore.cpp | 179 ++++++++++++++++++++++ Core/MIPS/x86/X64IRCompSystem.cpp | 142 +++++++++++++++++ Core/MIPS/x86/X64IRCompVec.cpp | 130 ++++++++++++++++ Core/MIPS/x86/X64IRJit.cpp | 42 ----- UWP/CoreUWP/CoreUWP.vcxproj | 6 + UWP/CoreUWP/CoreUWP.vcxproj.filters | 18 +++ android/jni/Android.mk | 6 + libretro/Makefile.common | 6 + 16 files changed, 1018 insertions(+), 44 deletions(-) create mode 100644 Core/MIPS/x86/X64IRCompALU.cpp create mode 100644 Core/MIPS/x86/X64IRCompBranch.cpp create mode 100644 Core/MIPS/x86/X64IRCompFPU.cpp create mode 100644 Core/MIPS/x86/X64IRCompLoadStore.cpp create mode 100644 Core/MIPS/x86/X64IRCompSystem.cpp create mode 100644 Core/MIPS/x86/X64IRCompVec.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index db9ae9f230..4d6033eada 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1612,6 +1612,12 @@ list(APPEND CoreExtra Core/MIPS/x86/RegCacheFPU.cpp Core/MIPS/x86/RegCacheFPU.h Core/MIPS/x86/X64IRAsm.cpp + Core/MIPS/x86/X64IRCompALU.cpp + Core/MIPS/x86/X64IRCompBranch.cpp + Core/MIPS/x86/X64IRCompFPU.cpp + Core/MIPS/x86/X64IRCompLoadStore.cpp + Core/MIPS/x86/X64IRCompSystem.cpp + Core/MIPS/x86/X64IRCompVec.cpp Core/MIPS/x86/X64IRJit.cpp Core/MIPS/x86/X64IRJit.h Core/MIPS/x86/X64IRRegCache.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index e011e967e8..bd3fb17ad7 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -606,6 +606,12 @@ + + + + + + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 27da24f9b4..15964c072c 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1252,6 +1252,24 @@ MIPS\x86 + + MIPS\x86 + + + MIPS\x86 + + + MIPS\x86 + + + MIPS\x86 + + + MIPS\x86 + + + MIPS\x86 + diff --git a/Core/MIPS/RiscV/RiscVCompBranch.cpp b/Core/MIPS/RiscV/RiscVCompBranch.cpp index f1126b0317..f8663a82c9 100644 --- a/Core/MIPS/RiscV/RiscVCompBranch.cpp +++ b/Core/MIPS/RiscV/RiscVCompBranch.cpp @@ -15,7 +15,6 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include "Core/MemMap.h" #include "Core/MIPS/RiscV/RiscVJit.h" #include "Core/MIPS/RiscV/RiscVRegCache.h" diff --git a/Core/MIPS/RiscV/RiscVCompFPU.cpp b/Core/MIPS/RiscV/RiscVCompFPU.cpp index c0cec762c4..3e36371c29 100644 --- a/Core/MIPS/RiscV/RiscVCompFPU.cpp +++ b/Core/MIPS/RiscV/RiscVCompFPU.cpp @@ -15,7 +15,6 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include "Core/MemMap.h" #include "Core/MIPS/RiscV/RiscVJit.h" #include "Core/MIPS/RiscV/RiscVRegCache.h" diff --git a/Core/MIPS/x86/X64IRCompALU.cpp b/Core/MIPS/x86/X64IRCompALU.cpp new file mode 100644 index 0000000000..b80e05f46a --- /dev/null +++ b/Core/MIPS/x86/X64IRCompALU.cpp @@ -0,0 +1,220 @@ +// Copyright (c) 2023- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "ppsspp_config.h" +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + +#include "Common/CPUDetect.h" +#include "Core/MemMap.h" +#include "Core/MIPS/x86/X64IRJit.h" +#include "Core/MIPS/x86/X64IRRegCache.h" + +// This file contains compilation for integer / arithmetic / logic related instructions. +// +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. No flags because that's in IR already. + +// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; } +#define CONDITIONAL_DISABLE {} +#define DISABLE { CompIR_Generic(inst); return; } +#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; } + +namespace MIPSComp { + +using namespace Gen; +using namespace X64IRJitConstants; + +void X64JitBackend::CompIR_Arith(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Add: + case IROp::Sub: + case IROp::AddConst: + case IROp::SubConst: + case IROp::Neg: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_Assign(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Mov: + case IROp::Ext8to32: + case IROp::Ext16to32: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_Bits(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::ReverseBits: + case IROp::BSwap16: + case IROp::BSwap32: + case IROp::Clz: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_Compare(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Slt: + case IROp::SltConst: + case IROp::SltU: + case IROp::SltUConst: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_CondAssign(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::MovZ: + case IROp::MovNZ: + case IROp::Max: + case IROp::Min: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_Div(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Div: + case IROp::DivU: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_HiLo(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::MtLo: + case IROp::MtHi: + case IROp::MfLo: + case IROp::MfHi: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_Logic(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::And: + case IROp::Or: + case IROp::Xor: + case IROp::AndConst: + case IROp::OrConst: + case IROp::XorConst: + case IROp::Not: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_Mult(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Mult: + case IROp::MultU: + case IROp::Madd: + case IROp::MaddU: + case IROp::Msub: + case IROp::MsubU: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_Shift(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Shl: + case IROp::Shr: + case IROp::Sar: + case IROp::Ror: + case IROp::ShlImm: + case IROp::ShrImm: + case IROp::SarImm: + case IROp::RorImm: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +} // namespace MIPSComp + +#endif diff --git a/Core/MIPS/x86/X64IRCompBranch.cpp b/Core/MIPS/x86/X64IRCompBranch.cpp new file mode 100644 index 0000000000..d54c6f7acb --- /dev/null +++ b/Core/MIPS/x86/X64IRCompBranch.cpp @@ -0,0 +1,82 @@ +// Copyright (c) 2023- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "ppsspp_config.h" +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + +#include "Core/MIPS/x86/X64IRJit.h" +#include "Core/MIPS/x86/X64IRRegCache.h" + +// This file contains compilation for exits. +// +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. No flags because that's in IR already. + +// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; } +#define CONDITIONAL_DISABLE {} +#define DISABLE { CompIR_Generic(inst); return; } +#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; } + +namespace MIPSComp { + +using namespace Gen; +using namespace X64IRJitConstants; + +void X64JitBackend::CompIR_Exit(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::ExitToConst: + case IROp::ExitToReg: + case IROp::ExitToPC: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_ExitIf(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::ExitToConstIfEq: + case IROp::ExitToConstIfNeq: + case IROp::ExitToConstIfGtZ: + case IROp::ExitToConstIfGeZ: + case IROp::ExitToConstIfLtZ: + case IROp::ExitToConstIfLeZ: + CompIR_Generic(inst); + break; + + case IROp::ExitToConstIfFpTrue: + case IROp::ExitToConstIfFpFalse: + // Note: not used. + DISABLE; + break; + + default: + INVALIDOP; + break; + } +} + +} // namespace MIPSComp + +#endif diff --git a/Core/MIPS/x86/X64IRCompFPU.cpp b/Core/MIPS/x86/X64IRCompFPU.cpp new file mode 100644 index 0000000000..e143865ca1 --- /dev/null +++ b/Core/MIPS/x86/X64IRCompFPU.cpp @@ -0,0 +1,199 @@ +// Copyright (c) 2023- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "ppsspp_config.h" +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + +#include "Core/MIPS/x86/X64IRJit.h" +#include "Core/MIPS/x86/X64IRRegCache.h" + +// This file contains compilation for floating point related instructions. +// +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. No flags because that's in IR already. + +// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; } +#define CONDITIONAL_DISABLE {} +#define DISABLE { CompIR_Generic(inst); return; } +#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; } + +namespace MIPSComp { + +using namespace Gen; +using namespace X64IRJitConstants; + +void X64JitBackend::CompIR_FArith(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::FAdd: + case IROp::FSub: + case IROp::FMul: + case IROp::FDiv: + case IROp::FSqrt: + case IROp::FNeg: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_FAssign(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::FMov: + case IROp::FAbs: + case IROp::FSign: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_FCompare(IRInst inst) { + CONDITIONAL_DISABLE; + + constexpr IRReg IRREG_VFPU_CC = IRREG_VFPU_CTRL_BASE + VFPU_CTRL_CC; + + switch (inst.op) { + case IROp::FCmp: + case IROp::FCmovVfpuCC: + case IROp::FCmpVfpuBit: + case IROp::FCmpVfpuAggregate: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_FCondAssign(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::FMin: + case IROp::FMax: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_FCvt(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::FCvtWS: + case IROp::FCvtSW: + case IROp::FCvtScaledWS: + case IROp::FCvtScaledSW: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_FRound(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::FRound: + case IROp::FTrunc: + case IROp::FCeil: + case IROp::FFloor: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_FSat(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::FSat0_1: + case IROp::FSatMinus1_1: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_FSpecial(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::FSin: + case IROp::FCos: + case IROp::FRSqrt: + case IROp::FRecip: + case IROp::FAsin: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_RoundingMode(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::RestoreRoundingMode: + RestoreRoundingMode(); + break; + + case IROp::ApplyRoundingMode: + ApplyRoundingMode(); + break; + + case IROp::UpdateRoundingMode: + // TODO: We might want to do something here? + break; + + default: + INVALIDOP; + break; + } +} + +} // namespace MIPSComp + +#endif diff --git a/Core/MIPS/x86/X64IRCompLoadStore.cpp b/Core/MIPS/x86/X64IRCompLoadStore.cpp new file mode 100644 index 0000000000..d80e5c40cb --- /dev/null +++ b/Core/MIPS/x86/X64IRCompLoadStore.cpp @@ -0,0 +1,179 @@ +// Copyright (c) 2023- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "ppsspp_config.h" +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + +#include "Core/MemMap.h" +#include "Core/MIPS/x86/X64IRJit.h" +#include "Core/MIPS/x86/X64IRRegCache.h" + +// This file contains compilation for load/store instructions. +// +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. No flags because that's in IR already. + +// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; } +#define CONDITIONAL_DISABLE {} +#define DISABLE { CompIR_Generic(inst); return; } +#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; } + +namespace MIPSComp { + +using namespace Gen; +using namespace X64IRJitConstants; + +void X64JitBackend::CompIR_CondStore(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Store32Conditional: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_FLoad(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::LoadFloat: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_FStore(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::StoreFloat: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_Load(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Load8: + case IROp::Load8Ext: + case IROp::Load16: + case IROp::Load16Ext: + case IROp::Load32: + case IROp::Load32Linked: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_LoadShift(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Load32Left: + case IROp::Load32Right: + // Should not happen if the pass to split is active. + DISABLE; + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_Store(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Store8: + case IROp::Store16: + case IROp::Store32: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_StoreShift(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Store32Left: + case IROp::Store32Right: + // Should not happen if the pass to split is active. + DISABLE; + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_VecLoad(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::LoadVec4: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_VecStore(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::StoreVec4: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +} // namespace MIPSComp + +#endif diff --git a/Core/MIPS/x86/X64IRCompSystem.cpp b/Core/MIPS/x86/X64IRCompSystem.cpp new file mode 100644 index 0000000000..5ff96d0c86 --- /dev/null +++ b/Core/MIPS/x86/X64IRCompSystem.cpp @@ -0,0 +1,142 @@ +// Copyright (c) 2023- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "ppsspp_config.h" +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + +#include "Common/Profiler/Profiler.h" +#include "Core/Core.h" +#include "Core/HLE/HLE.h" +#include "Core/HLE/ReplaceTables.h" +#include "Core/MemMap.h" +#include "Core/MIPS/x86/X64IRJit.h" +#include "Core/MIPS/x86/X64IRRegCache.h" + +// This file contains compilation for basic PC/downcount accounting, syscalls, debug funcs, etc. +// +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. No flags because that's in IR already. + +// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; } +#define CONDITIONAL_DISABLE {} +#define DISABLE { CompIR_Generic(inst); return; } +#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; } + +namespace MIPSComp { + +using namespace Gen; +using namespace X64IRJitConstants; + +void X64JitBackend::CompIR_Basic(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Downcount: + //ADD(32, R(DOWNCOUNTREG), R(DOWNCOUNTREG), Imm32(-(s32)inst.constant)); + SUB(32, MDisp(CTXREG, -128 + offsetof(MIPSState, downcount)), Imm32((s32)inst.constant)); + break; + + case IROp::SetConst: + regs_.SetGPRImm(inst.dest, inst.constant); + break; + + case IROp::SetConstF: + case IROp::SetPC: + case IROp::SetPCConst: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_Breakpoint(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Breakpoint: + case IROp::MemoryCheck: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_System(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Syscall: + case IROp::CallReplacement: + case IROp::Break: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_Transfer(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::SetCtrlVFPU: + case IROp::SetCtrlVFPUReg: + case IROp::SetCtrlVFPUFReg: + case IROp::FpCondFromReg: + case IROp::FpCondToReg: + case IROp::FpCtrlFromReg: + case IROp::FpCtrlToReg: + case IROp::VfpuCtrlToReg: + case IROp::FMovFromGPR: + case IROp::FMovToGPR: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_ValidateAddress(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::ValidateAddress8: + case IROp::ValidateAddress16: + case IROp::ValidateAddress32: + case IROp::ValidateAddress128: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +} // namespace MIPSComp + +#endif diff --git a/Core/MIPS/x86/X64IRCompVec.cpp b/Core/MIPS/x86/X64IRCompVec.cpp new file mode 100644 index 0000000000..051c8c0fd7 --- /dev/null +++ b/Core/MIPS/x86/X64IRCompVec.cpp @@ -0,0 +1,130 @@ +// Copyright (c) 2023- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "ppsspp_config.h" +#if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) + +#include +#include "Core/MemMap.h" +#include "Core/MIPS/x86/X64IRJit.h" +#include "Core/MIPS/x86/X64IRRegCache.h" + +// This file contains compilation for vector instructions. +// +// All functions should have CONDITIONAL_DISABLE, so we can narrow things down to a file quickly. +// Currently known non working ones should have DISABLE. No flags because that's in IR already. + +// #define CONDITIONAL_DISABLE { CompIR_Generic(inst); return; } +#define CONDITIONAL_DISABLE {} +#define DISABLE { CompIR_Generic(inst); return; } +#define INVALIDOP { _assert_msg_(false, "Invalid IR inst %d", (int)inst.op); CompIR_Generic(inst); return; } + +namespace MIPSComp { + +using namespace Gen; +using namespace X64IRJitConstants; + +void X64JitBackend::CompIR_VecArith(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Vec4Add: + case IROp::Vec4Sub: + case IROp::Vec4Mul: + case IROp::Vec4Div: + case IROp::Vec4Scale: + case IROp::Vec4Neg: + case IROp::Vec4Abs: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_VecAssign(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Vec4Init: + case IROp::Vec4Shuffle: + case IROp::Vec4Blend: + case IROp::Vec4Mov: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_VecClamp(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Vec4ClampToZero: + case IROp::Vec2ClampToZero: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_VecHoriz(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Vec4Dot: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +void X64JitBackend::CompIR_VecPack(IRInst inst) { + CONDITIONAL_DISABLE; + + switch (inst.op) { + case IROp::Vec2Unpack16To31: + case IROp::Vec4Pack32To8: + case IROp::Vec2Pack31To16: + case IROp::Vec4Unpack8To32: + case IROp::Vec2Unpack16To32: + case IROp::Vec4DuplicateUpperBitsAndShift1: + case IROp::Vec4Pack31To8: + case IROp::Vec2Pack32To16: + CompIR_Generic(inst); + break; + + default: + INVALIDOP; + break; + } +} + +} // namespace MIPSComp + +#endif diff --git a/Core/MIPS/x86/X64IRJit.cpp b/Core/MIPS/x86/X64IRJit.cpp index 7d7c90b6a0..54f5e4c7b2 100644 --- a/Core/MIPS/x86/X64IRJit.cpp +++ b/Core/MIPS/x86/X64IRJit.cpp @@ -325,48 +325,6 @@ void X64JitBackend::LoadStaticRegisters() { } } -// TODO: Move out to files. -void X64JitBackend::CompIR_Arith(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_Assign(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_Basic(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_Bits(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_Breakpoint(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_Compare(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_CondAssign(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_CondStore(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_Div(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_Exit(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_ExitIf(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_FArith(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_FAssign(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_FCompare(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_FCondAssign(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_FCvt(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_FLoad(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_FRound(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_FSat(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_FSpecial(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_FStore(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_HiLo(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_Load(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_LoadShift(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_Logic(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_Mult(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_RoundingMode(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_Shift(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_Store(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_StoreShift(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_System(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_Transfer(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_VecArith(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_VecAssign(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_VecClamp(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_VecHoriz(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_VecLoad(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_VecPack(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_VecStore(IRInst inst) { CompIR_Generic(inst); } -void X64JitBackend::CompIR_ValidateAddress(IRInst inst) { CompIR_Generic(inst); } - } // namespace MIPSComp #endif diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index a2914b2615..9120724fc7 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -576,6 +576,12 @@ + + + + + + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index 58215c1e3d..91839d6316 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -192,6 +192,24 @@ MIPS\x86 + + MIPS\x86 + + + MIPS\x86 + + + MIPS\x86 + + + MIPS\x86 + + + MIPS\x86 + + + MIPS\x86 + MIPS\x86 diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 371872c544..95e98a34fb 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -304,6 +304,12 @@ ARCH_FILES := \ $(SRC)/Core/MIPS/x86/RegCache.cpp \ $(SRC)/Core/MIPS/x86/RegCacheFPU.cpp \ $(SRC)/Core/MIPS/x86/X64IRAsm.cpp \ + $(SRC)/Core/MIPS/x86/X64IRCompALU.cpp \ + $(SRC)/Core/MIPS/x86/X64IRCompBranch.cpp \ + $(SRC)/Core/MIPS/x86/X64IRCompFPU.cpp \ + $(SRC)/Core/MIPS/x86/X64IRCompLoadStore.cpp \ + $(SRC)/Core/MIPS/x86/X64IRCompSystem.cpp \ + $(SRC)/Core/MIPS/x86/X64IRCompVec.cpp \ $(SRC)/Core/MIPS/x86/X64IRJit.cpp \ $(SRC)/Core/MIPS/x86/X64IRRegCache.cpp \ $(SRC)/GPU/Common/VertexDecoderX86.cpp \ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 020c2652d2..f7c2c98a29 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -803,6 +803,12 @@ ifeq ($(WITH_DYNAREC),1) $(COREDIR)/MIPS/x86/RegCache.cpp \ $(COREDIR)/MIPS/x86/RegCacheFPU.cpp \ $(COREDIR)/MIPS/x86/X64IRAsm.cpp \ + $(COREDIR)/MIPS/x86/X64IRCompALU.cpp \ + $(COREDIR)/MIPS/x86/X64IRCompBranch.cpp \ + $(COREDIR)/MIPS/x86/X64IRCompFPU.cpp \ + $(COREDIR)/MIPS/x86/X64IRCompLoadStore.cpp \ + $(COREDIR)/MIPS/x86/X64IRCompSystem.cpp \ + $(COREDIR)/MIPS/x86/X64IRCompVec.cpp \ $(COREDIR)/MIPS/x86/X64IRJit.cpp \ $(COREDIR)/MIPS/x86/X64IRRegCache.cpp \ $(GPUDIR)/Common/VertexDecoderX86.cpp From a47b4424e595fedf62aad6f0d9e66c72d82215b6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 19:34:52 -0700 Subject: [PATCH 19/36] x86jit: Fix some silly mistakes. --- Core/MIPS/x86/X64IRAsm.cpp | 5 ++++- Core/MIPS/x86/X64IRRegCache.cpp | 15 +++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Core/MIPS/x86/X64IRAsm.cpp b/Core/MIPS/x86/X64IRAsm.cpp index 9019f57cb8..b9a24a5e05 100644 --- a/Core/MIPS/x86/X64IRAsm.cpp +++ b/Core/MIPS/x86/X64IRAsm.cpp @@ -171,7 +171,10 @@ void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) { // Debug if (enableDebug) { #if PPSSPP_ARCH(AMD64) - ABI_CallFunctionAA(reinterpret_cast(&ShowPC), R(MEMBASEREG), R(JITBASEREG)); + if (jo.reserveR15ForAsm) + ABI_CallFunctionAA(reinterpret_cast(&ShowPC), R(MEMBASEREG), R(JITBASEREG)); + else + ABI_CallFunctionAC(reinterpret_cast(&ShowPC), R(MEMBASEREG), (u32)jitbase); #else ABI_CallFunctionCC(reinterpret_cast(&ShowPC), Menory::base, (u32)jitbase); #endif diff --git a/Core/MIPS/x86/X64IRRegCache.cpp b/Core/MIPS/x86/X64IRRegCache.cpp index 4deae084b4..d79e1de69e 100644 --- a/Core/MIPS/x86/X64IRRegCache.cpp +++ b/Core/MIPS/x86/X64IRRegCache.cpp @@ -58,17 +58,20 @@ const int *X64IRRegCache::GetAllocationOrder(MIPSLoc type, int &count, int &base #else RBP, R8, R9, R10, R11, R12, R13, #endif + // Intentionally last. + R15, #elif PPSSPP_ARCH(X86) ESI, EDI, EDX, ECX, EBX, #endif - // Intentionally last. - R15, }; - if (!jo_->reserveR15ForAsm) +#if !PPSSPP_ARCH(X86) + if (jo_->reserveR15ForAsm) { count = ARRAY_SIZE(allocationOrder) - 1; - else - count = ARRAY_SIZE(allocationOrder); + return allocationOrder; + } +#endif + count = ARRAY_SIZE(allocationOrder); return allocationOrder; } else if (type == MIPSLoc::FREG) { base = -NUM_X_REGS; @@ -285,7 +288,7 @@ X64Reg X64IRRegCache::R(IRReg mipsReg) { if (mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM) { return FromNativeReg(mr[mipsReg].nReg); } else { - ERROR_LOG_REPORT(JIT, "Reg %i not in riscv reg", mipsReg); + ERROR_LOG_REPORT(JIT, "Reg %i not in x64 reg", mipsReg); return INVALID_REG; // BAAAD } } From 4e7f8cf2131ccafd8be0ee70115419efac41bb30 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 20:19:51 -0700 Subject: [PATCH 20/36] x86jit: Implement load/store. --- Core/MIPS/x86/X64IRCompLoadStore.cpp | 89 +++++++++++++++++++++++++++- Core/MIPS/x86/X64IRJit.h | 4 -- Core/MIPS/x86/X64IRRegCache.cpp | 16 +++-- Core/MIPS/x86/X64IRRegCache.h | 8 ++- 4 files changed, 104 insertions(+), 13 deletions(-) diff --git a/Core/MIPS/x86/X64IRCompLoadStore.cpp b/Core/MIPS/x86/X64IRCompLoadStore.cpp index d80e5c40cb..1576c63706 100644 --- a/Core/MIPS/x86/X64IRCompLoadStore.cpp +++ b/Core/MIPS/x86/X64IRCompLoadStore.cpp @@ -82,14 +82,54 @@ void X64JitBackend::CompIR_FStore(IRInst inst) { void X64JitBackend::CompIR_Load(IRInst inst) { CONDITIONAL_DISABLE; + regs_.SpillLockGPR(inst.dest, inst.src1); + OpArg addrArg; + if (inst.src1 == MIPS_REG_ZERO) { +#ifdef MASKED_PSP_MEMORY + inst.constant &= Memory::MEMVIEW32_MASK; +#endif +#if PPSSPP_ARCH(AMD64) + addrArg = MDisp(MEMBASEREG, inst.constant & 0x7FFFFFFF); +#else + addrArg = M(Memory::base + inst.constant); +#endif + } else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) { + X64Reg src1 = regs_.MapGPRAsPointer(inst.src1); + addrArg = MDisp(src1, inst.constant & 0x7FFFFFFF); + } else { + regs_.MapGPR(inst.src1); + addrArg = MComplex(MEMBASEREG, regs_.RX(inst.src1), SCALE_1, inst.constant & 0x7FFFFFFF); + } + // With NOINIT, MapReg won't subtract MEMBASEREG even if dest == src1. + regs_.MapGPR(inst.dest, MIPSMap::NOINIT); + + // TODO: Safe memory? Or enough to have crash handler + validate? + switch (inst.op) { case IROp::Load8: + MOVZX(32, 8, regs_.RX(inst.dest), addrArg); + break; + case IROp::Load8Ext: + MOVSX(32, 8, regs_.RX(inst.dest), addrArg); + break; + case IROp::Load16: + MOVZX(32, 16, regs_.RX(inst.dest), addrArg); + break; + case IROp::Load16Ext: + MOVSX(32, 16, regs_.RX(inst.dest), addrArg); + break; + case IROp::Load32: + MOV(32, regs_.R(inst.dest), addrArg); + break; + case IROp::Load32Linked: - CompIR_Generic(inst); + if (inst.dest != MIPS_REG_ZERO) + MOV(32, regs_.R(inst.dest), addrArg); + regs_.SetGPRImm(IRREG_LLBIT, 1); break; default: @@ -117,11 +157,56 @@ void X64JitBackend::CompIR_LoadShift(IRInst inst) { void X64JitBackend::CompIR_Store(IRInst inst) { CONDITIONAL_DISABLE; + regs_.SpillLockGPR(inst.src3, inst.src1); + OpArg addrArg; + if (inst.src1 == MIPS_REG_ZERO) { +#ifdef MASKED_PSP_MEMORY + inst.constant &= Memory::MEMVIEW32_MASK; +#endif +#if PPSSPP_ARCH(AMD64) + addrArg = MDisp(MEMBASEREG, inst.constant & 0x7FFFFFFF); +#else + addrArg = M(Memory::base + inst.constant); +#endif + } else if ((jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) { + X64Reg src1 = regs_.MapGPRAsPointer(inst.src1); + addrArg = MDisp(src1, inst.constant & 0x7FFFFFFF); + } else { + regs_.MapGPR(inst.src1); + addrArg = MComplex(MEMBASEREG, regs_.RX(inst.src1), SCALE_1, inst.constant & 0x7FFFFFFF); + } + + OpArg valueArg; + X64Reg valueReg = regs_.TryMapTempImm(inst.src3); + if (valueReg != INVALID_REG) { + valueArg = R(valueReg); + } else if (regs_.IsGPRImm(inst.src3)) { + u32 imm = regs_.GetGPRImm(inst.src3); + switch (inst.op) { + case IROp::Store8: valueArg = Imm8((u8)imm); break; + case IROp::Store16: valueArg = Imm16((u16)imm); break; + case IROp::Store32: valueArg = Imm32(imm); break; + default: + INVALIDOP; + break; + } + } else { + valueArg = R(regs_.MapGPR(inst.src3)); + } + + // TODO: Safe memory? Or enough to have crash handler + validate? + switch (inst.op) { case IROp::Store8: + MOV(8, addrArg, valueArg); + break; + case IROp::Store16: + MOV(16, addrArg, valueArg); + break; + case IROp::Store32: - CompIR_Generic(inst); + MOV(32, addrArg, valueArg); break; default: diff --git a/Core/MIPS/x86/X64IRJit.h b/Core/MIPS/x86/X64IRJit.h index 4144ece248..afccf2bd5d 100644 --- a/Core/MIPS/x86/X64IRJit.h +++ b/Core/MIPS/x86/X64IRJit.h @@ -106,10 +106,6 @@ private: void CompIR_VecStore(IRInst inst) override; void CompIR_ValidateAddress(IRInst inst) override; - void SetScratch1ToSrc1Address(IRReg src1); - // Modifies SCRATCH regs. - int32_t AdjustForAddressOffset(Gen::X64Reg *reg, int32_t constant, int32_t range = 0); - JitOptions &jo; X64IRRegCache regs_; diff --git a/Core/MIPS/x86/X64IRRegCache.cpp b/Core/MIPS/x86/X64IRRegCache.cpp index d79e1de69e..dfd7d62294 100644 --- a/Core/MIPS/x86/X64IRRegCache.cpp +++ b/Core/MIPS/x86/X64IRRegCache.cpp @@ -126,7 +126,7 @@ X64Reg X64IRRegCache::TryMapTempImm(IRReg r) { _dbg_assert_(IsValidGPR(r)); // If already mapped, no need for a temporary. if (IsGPRMapped(r)) { - return R(r); + return RX(r); } if (mr[r].loc == MIPSLoc::IMM) { @@ -282,7 +282,15 @@ void X64IRRegCache::StoreRegValue(IRReg mreg, uint32_t imm) { emit_->MOV(32, MDisp(CTXREG, -128 + GetMipsRegOffset(mreg)), ::R(storeReg)); } -X64Reg X64IRRegCache::R(IRReg mipsReg) { +OpArg X64IRRegCache::R(IRReg mipsReg) { + return ::R(RX(mipsReg)); +} + +OpArg X64IRRegCache::F(IRReg mipsReg) { + return ::R(FX(mipsReg)); +} + +X64Reg X64IRRegCache::RX(IRReg mipsReg) { _dbg_assert_(IsValidGPR(mipsReg)); _dbg_assert_(mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM); if (mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM) { @@ -293,7 +301,7 @@ X64Reg X64IRRegCache::R(IRReg mipsReg) { } } -X64Reg X64IRRegCache::RPtr(IRReg mipsReg) { +X64Reg X64IRRegCache::RXPtr(IRReg mipsReg) { _dbg_assert_(IsValidGPR(mipsReg)); _dbg_assert_(mr[mipsReg].loc == MIPSLoc::REG || mr[mipsReg].loc == MIPSLoc::REG_IMM || mr[mipsReg].loc == MIPSLoc::REG_AS_PTR); if (mr[mipsReg].loc == MIPSLoc::REG_AS_PTR) { @@ -313,7 +321,7 @@ X64Reg X64IRRegCache::RPtr(IRReg mipsReg) { } } -X64Reg X64IRRegCache::F(IRReg mipsReg) { +X64Reg X64IRRegCache::FX(IRReg mipsReg) { _dbg_assert_(IsValidFPR(mipsReg)); _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::FREG); if (mr[mipsReg + 32].loc == MIPSLoc::FREG) { diff --git a/Core/MIPS/x86/X64IRRegCache.h b/Core/MIPS/x86/X64IRRegCache.h index 5054caeaf3..2c644275ff 100644 --- a/Core/MIPS/x86/X64IRRegCache.h +++ b/Core/MIPS/x86/X64IRRegCache.h @@ -58,9 +58,11 @@ public: Gen::X64Reg GetAndLockTempR(); - Gen::X64Reg R(IRReg preg); // Returns a cached register, while checking that it's NOT mapped as a pointer - Gen::X64Reg RPtr(IRReg preg); // Returns a cached register, if it has been mapped as a pointer - Gen::X64Reg F(IRReg preg); + Gen::OpArg R(IRReg preg); + Gen::OpArg F(IRReg preg); + Gen::X64Reg RX(IRReg preg); // Returns a cached register, while checking that it's NOT mapped as a pointer + Gen::X64Reg RXPtr(IRReg preg); // Returns a cached register, if it has been mapped as a pointer + Gen::X64Reg FX(IRReg preg); protected: const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const override; From 08ea31f405333986d36b6c3bdb6ec0784eda90dd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 22:10:47 -0700 Subject: [PATCH 21/36] x86jit: Improve debug disasm. --- Core/MIPS/RiscV/RiscVJit.cpp | 30 ++++++++++++++++-------------- Core/MIPS/x86/X64IRJit.cpp | 11 ++++++----- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/Core/MIPS/RiscV/RiscVJit.cpp b/Core/MIPS/RiscV/RiscVJit.cpp index d59927b8dc..acd4e10edf 100644 --- a/Core/MIPS/RiscV/RiscVJit.cpp +++ b/Core/MIPS/RiscV/RiscVJit.cpp @@ -16,7 +16,6 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include -#include "ext/riscv-disas.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/RiscV/RiscVJit.h" @@ -76,12 +75,12 @@ bool RiscVJitBackend::CompileBlock(IRBlock *block, int block_num, bool preload) regs_.Start(block); - std::map addresses; + std::map addresses; for (int i = 0; i < block->GetNumInstructions(); ++i) { const IRInst &inst = block->GetInstructions()[i]; regs_.SetIRIndex(i); // TODO: This might be a little wasteful when compiling if we're not debugging jit... - addresses[GetCodePtr()] = inst; + addresses[GetCodePtr()] = i; CompileIRInst(inst); @@ -129,23 +128,26 @@ bool RiscVJitBackend::CompileBlock(IRBlock *block, int block_num, bool preload) if (logBlocks_ > 0) { --logBlocks_; - INFO_LOG(JIT, "=============== RISCV (%d bytes) ===============", len); + INFO_LOG(JIT, "=============== RISCV (%08x, %d bytes) ===============", startPC, len); for (const u8 *p = blockStart; p < GetCodePointer(); ) { - char temp[512]; - rv_inst inst; - size_t len; - auto it = addresses.find(p); if (it != addresses.end()) { - DisassembleIR(temp, sizeof(temp), it->second); - INFO_LOG(JIT, "IR: # %s", temp); + const IRInst &inst = block->GetInstructions()[it->second]; + + char temp[512]; + DisassembleIR(temp, sizeof(temp), inst); + INFO_LOG(JIT, "IR: #%d %s", it->second, temp); } - riscv_inst_fetch(p, &inst, &len); - riscv_disasm_inst(temp, sizeof(temp), rv64, (uintptr_t)p, inst); - p += len; + auto next = std::next(it); + const u8 *nextp = next == addresses.end() ? GetCodePointer() : next->first; - INFO_LOG(JIT, "RV: %s", temp); +#if PPSSPP_ARCH(RISCV64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__)) + auto lines = DisassembleRV64(p, (int)(nextp - p)); + for (const auto &line : lines) + INFO_LOG(JIT, "RV: %s", line.c_str()); +#endif + p = nextp; } } diff --git a/Core/MIPS/x86/X64IRJit.cpp b/Core/MIPS/x86/X64IRJit.cpp index 54f5e4c7b2..3cb57d5b2c 100644 --- a/Core/MIPS/x86/X64IRJit.cpp +++ b/Core/MIPS/x86/X64IRJit.cpp @@ -78,12 +78,12 @@ bool X64JitBackend::CompileBlock(IRBlock *block, int block_num, bool preload) { regs_.Start(block); - std::map addresses; + std::map addresses; for (int i = 0; i < block->GetNumInstructions(); ++i) { const IRInst &inst = block->GetInstructions()[i]; regs_.SetIRIndex(i); // TODO: This might be a little wasteful when compiling if we're not debugging jit... - addresses[GetCodePtr()] = inst; + addresses[GetCodePtr()] = i; CompileIRInst(inst); @@ -127,14 +127,15 @@ bool X64JitBackend::CompileBlock(IRBlock *block, int block_num, bool preload) { if (logBlocks_ > 0) { --logBlocks_; - INFO_LOG(JIT, "=============== x86 (%d bytes) ===============", len); + INFO_LOG(JIT, "=============== x86 (%08x, %d bytes) ===============", startPC, len); for (const u8 *p = blockStart; p < GetCodePointer(); ) { auto it = addresses.find(p); if (it != addresses.end()) { + const IRInst &inst = block->GetInstructions()[it->second]; char temp[512]; - DisassembleIR(temp, sizeof(temp), it->second); - INFO_LOG(JIT, "IR: # %s", temp); + DisassembleIR(temp, sizeof(temp), inst); + INFO_LOG(JIT, "IR: #%d %s", it->second, temp); } auto next = std::next(it); From 5045cf012e7bd0386dbe21d6a0cfd8c80552315d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 22:11:10 -0700 Subject: [PATCH 22/36] x86jit: Fix flushing of zero register. --- Core/MIPS/IR/IRRegCache.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index aaa8fde5b8..985adad7a2 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -437,12 +437,13 @@ IRNativeReg IRNativeRegCacheBase::FindBestToSpill(MIPSLoc type, bool unusedOnly, void IRNativeRegCacheBase::DiscardNativeReg(IRNativeReg nreg) { _assert_msg_(nreg >= 0 && nreg < config_.totalNativeRegs, "DiscardNativeReg on invalid register %d", nreg); if (nr[nreg].mipsReg != IRREG_INVALID) { - _assert_(nr[nreg].mipsReg != MIPS_REG_ZERO); int8_t lanes = 0; for (IRReg m = nr[nreg].mipsReg; mr[m].nReg == nreg && m < IRREG_INVALID; ++m) lanes++; if (mr[nr[nreg].mipsReg].isStatic) { + _assert_(nr[nreg].mipsReg != MIPS_REG_ZERO); + int numStatics; const StaticAllocation *statics = GetStaticAllocations(numStatics); @@ -581,6 +582,9 @@ void IRNativeRegCacheBase::FlushAll(bool gprs, bool fprs) { // Note: make sure not to change the registers when flushing. // Branching code may expect the native reg to retain its value. + if (!mr[MIPS_REG_ZERO].isStatic && mr[MIPS_REG_ZERO].nReg != -1) + DiscardNativeReg(mr[MIPS_REG_ZERO].nReg); + for (int i = 1; i < TOTAL_MAPPABLE_IRREGS; i++) { IRReg mipsReg = (IRReg)i; if (!fprs && i >= 32 && IsValidFPR(mipsReg)) From 104b6d8c15b5e63fe9ce35b6e82cdb2571b6faeb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 22:11:51 -0700 Subject: [PATCH 23/36] x86jit: Implement some basic arithmetic. --- Core/MIPS/x86/X64IRCompALU.cpp | 83 +++++++++++++++++++++++++++++++ Core/MIPS/x86/X64IRCompBranch.cpp | 4 ++ Core/MIPS/x86/X64IRRegCache.cpp | 4 ++ Core/MIPS/x86/X64IRRegCache.h | 1 + 4 files changed, 92 insertions(+) diff --git a/Core/MIPS/x86/X64IRCompALU.cpp b/Core/MIPS/x86/X64IRCompALU.cpp index b80e05f46a..24f9c6d3e6 100644 --- a/Core/MIPS/x86/X64IRCompALU.cpp +++ b/Core/MIPS/x86/X64IRCompALU.cpp @@ -41,11 +41,73 @@ using namespace X64IRJitConstants; void X64JitBackend::CompIR_Arith(IRInst inst) { CONDITIONAL_DISABLE; + bool allowPtrMath = inst.constant <= 0x7FFFFFFF; +#ifdef MASKED_PSP_MEMORY + // Since we modify it, we can't safely. + allowPtrMath = false; +#endif + switch (inst.op) { case IROp::Add: + regs_.Map(inst); + if (inst.dest == inst.src2) { + ADD(32, regs_.R(inst.dest), regs_.R(inst.src1)); + } else if (inst.dest == inst.src1) { + ADD(32, regs_.R(inst.dest), regs_.R(inst.src2)); + } else { + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + ADD(32, regs_.R(inst.dest), regs_.R(inst.src2)); + } + break; + case IROp::Sub: + if (regs_.IsGPRImm(inst.src1) && regs_.GetGPRImm(inst.src1) == 0) { + // TODO: Might be nice to have a pass to turn this into Neg. + // Special cased to avoid wasting a reg on zero. + regs_.MapGPR(inst.src2); + regs_.MapGPR(inst.dest, MIPSMap::NOINIT); + if (inst.dest != inst.src2) + MOV(32, regs_.R(inst.dest), regs_.R(inst.src2)); + NEG(32, regs_.R(inst.dest)); + } else if (inst.dest == inst.src2) { + regs_.Map(inst); + MOV(32, R(SCRATCH1), regs_.R(inst.src2)); + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + SUB(32, regs_.R(inst.dest), R(SCRATCH1)); + } else if (inst.dest == inst.src1) { + regs_.Map(inst); + SUB(32, regs_.R(inst.dest), regs_.R(inst.src2)); + } else { + regs_.Map(inst); + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + SUB(32, regs_.R(inst.dest), regs_.R(inst.src2)); + } + break; + case IROp::AddConst: + if (regs_.IsGPRMappedAsPointer(inst.dest) && inst.dest == inst.src1 && allowPtrMath) { + regs_.MarkGPRAsPointerDirty(inst.dest); + ADD(PTRBITS, regs_.RPtr(inst.dest), SImmAuto(inst.constant)); + } else { + regs_.Map(inst); + if (inst.dest != inst.src1) + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + ADD(32, regs_.R(inst.dest), SImmAuto(inst.constant)); + } + break; + case IROp::SubConst: + if (regs_.IsGPRMappedAsPointer(inst.dest) && inst.dest == inst.src1 && allowPtrMath) { + regs_.MarkGPRAsPointerDirty(inst.dest); + SUB(PTRBITS, regs_.RPtr(inst.dest), SImmAuto(inst.constant)); + } else { + regs_.Map(inst); + if (inst.dest != inst.src1) + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + SUB(32, regs_.R(inst.dest), SImmAuto(inst.constant)); + } + break; + case IROp::Neg: CompIR_Generic(inst); break; @@ -61,6 +123,12 @@ void X64JitBackend::CompIR_Assign(IRInst inst) { switch (inst.op) { case IROp::Mov: + if (inst.dest != inst.src1) { + regs_.Map(inst); + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + } + break; + case IROp::Ext8to32: case IROp::Ext16to32: CompIR_Generic(inst); @@ -162,8 +230,23 @@ void X64JitBackend::CompIR_Logic(IRInst inst) { case IROp::And: case IROp::Or: case IROp::Xor: + CompIR_Generic(inst); + break; + case IROp::AndConst: + regs_.Map(inst); + if (inst.dest != inst.src1) + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + AND(32, regs_.R(inst.dest), UImmAuto(inst.constant)); + break; + case IROp::OrConst: + regs_.Map(inst); + if (inst.dest != inst.src1) + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + OR(32, regs_.R(inst.dest), UImmAuto(inst.constant)); + break; + case IROp::XorConst: case IROp::Not: CompIR_Generic(inst); diff --git a/Core/MIPS/x86/X64IRCompBranch.cpp b/Core/MIPS/x86/X64IRCompBranch.cpp index d54c6f7acb..ffa1e60f69 100644 --- a/Core/MIPS/x86/X64IRCompBranch.cpp +++ b/Core/MIPS/x86/X64IRCompBranch.cpp @@ -41,6 +41,10 @@ void X64JitBackend::CompIR_Exit(IRInst inst) { switch (inst.op) { case IROp::ExitToConst: + FlushAll(); + WriteConstExit(inst.constant); + break; + case IROp::ExitToReg: case IROp::ExitToPC: CompIR_Generic(inst); diff --git a/Core/MIPS/x86/X64IRRegCache.cpp b/Core/MIPS/x86/X64IRRegCache.cpp index dfd7d62294..17a03d6292 100644 --- a/Core/MIPS/x86/X64IRRegCache.cpp +++ b/Core/MIPS/x86/X64IRRegCache.cpp @@ -286,6 +286,10 @@ OpArg X64IRRegCache::R(IRReg mipsReg) { return ::R(RX(mipsReg)); } +OpArg X64IRRegCache::RPtr(IRReg mipsReg) { + return ::R(RXPtr(mipsReg)); +} + OpArg X64IRRegCache::F(IRReg mipsReg) { return ::R(FX(mipsReg)); } diff --git a/Core/MIPS/x86/X64IRRegCache.h b/Core/MIPS/x86/X64IRRegCache.h index 2c644275ff..036fc4fbe5 100644 --- a/Core/MIPS/x86/X64IRRegCache.h +++ b/Core/MIPS/x86/X64IRRegCache.h @@ -59,6 +59,7 @@ public: Gen::X64Reg GetAndLockTempR(); Gen::OpArg R(IRReg preg); + Gen::OpArg RPtr(IRReg preg); Gen::OpArg F(IRReg preg); Gen::X64Reg RX(IRReg preg); // Returns a cached register, while checking that it's NOT mapped as a pointer Gen::X64Reg RXPtr(IRReg preg); // Returns a cached register, if it has been mapped as a pointer From 2b914046ffa8bec0585ad15b14227dc537ca03b8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 20 Aug 2023 22:25:00 -0700 Subject: [PATCH 24/36] x86jit: Implement most exits. --- Core/MIPS/x86/X64IRCompBranch.cpp | 65 ++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/Core/MIPS/x86/X64IRCompBranch.cpp b/Core/MIPS/x86/X64IRCompBranch.cpp index ffa1e60f69..367336eee9 100644 --- a/Core/MIPS/x86/X64IRCompBranch.cpp +++ b/Core/MIPS/x86/X64IRCompBranch.cpp @@ -39,6 +39,7 @@ using namespace X64IRJitConstants; void X64JitBackend::CompIR_Exit(IRInst inst) { CONDITIONAL_DISABLE; + X64Reg exitReg = INVALID_REG; switch (inst.op) { case IROp::ExitToConst: FlushAll(); @@ -46,6 +47,12 @@ void X64JitBackend::CompIR_Exit(IRInst inst) { break; case IROp::ExitToReg: + exitReg = regs_.MapGPR(inst.src1); + FlushAll(); + MOV(32, R(SCRATCH1), R(exitReg)); + JMP(dispatcherPCInSCRATCH1_, true); + break; + case IROp::ExitToPC: CompIR_Generic(inst); break; @@ -59,14 +66,70 @@ void X64JitBackend::CompIR_Exit(IRInst inst) { void X64JitBackend::CompIR_ExitIf(IRInst inst) { CONDITIONAL_DISABLE; + X64Reg lhs = INVALID_REG; + X64Reg rhs = INVALID_REG; + FixupBranch fixup; switch (inst.op) { case IROp::ExitToConstIfEq: case IROp::ExitToConstIfNeq: + regs_.Map(inst); + lhs = regs_.RX(inst.src1); + rhs = regs_.RX(inst.src2); + // This won't change those regs, intentionally. It might affect flags, though. + FlushAll(); + + CMP(32, R(lhs), R(rhs)); + switch (inst.op) { + case IROp::ExitToConstIfEq: + fixup = J_CC(CC_NE); + break; + + case IROp::ExitToConstIfNeq: + fixup = J_CC(CC_E); + break; + + default: + INVALIDOP; + break; + } + + WriteConstExit(inst.constant); + SetJumpTarget(fixup); + break; + case IROp::ExitToConstIfGtZ: case IROp::ExitToConstIfGeZ: case IROp::ExitToConstIfLtZ: case IROp::ExitToConstIfLeZ: - CompIR_Generic(inst); + regs_.Map(inst); + lhs = regs_.RX(inst.src1); + FlushAll(); + + CMP(32, R(lhs), Imm32(0)); + switch (inst.op) { + case IROp::ExitToConstIfGtZ: + fixup = J_CC(CC_LE, lhs); + break; + + case IROp::ExitToConstIfGeZ: + fixup = J_CC(CC_L, lhs); + break; + + case IROp::ExitToConstIfLtZ: + fixup = J_CC(CC_GE, lhs); + break; + + case IROp::ExitToConstIfLeZ: + fixup = J_CC(CC_G, lhs); + break; + + default: + INVALIDOP; + break; + } + + WriteConstExit(inst.constant); + SetJumpTarget(fixup); break; case IROp::ExitToConstIfFpTrue: From 538832940a2e6fb9604640df5e6a76d2d15f53b3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 21 Aug 2023 00:07:42 -0700 Subject: [PATCH 25/36] x86jit: Implement some shifts. --- Core/MIPS/x86/X64IRCompALU.cpp | 72 ++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/Core/MIPS/x86/X64IRCompALU.cpp b/Core/MIPS/x86/X64IRCompALU.cpp index 24f9c6d3e6..7809a217b4 100644 --- a/Core/MIPS/x86/X64IRCompALU.cpp +++ b/Core/MIPS/x86/X64IRCompALU.cpp @@ -285,13 +285,77 @@ void X64JitBackend::CompIR_Shift(IRInst inst) { case IROp::Shr: case IROp::Sar: case IROp::Ror: - case IROp::ShlImm: - case IROp::ShrImm: - case IROp::SarImm: - case IROp::RorImm: CompIR_Generic(inst); break; + case IROp::ShlImm: + // Shouldn't happen, but let's be safe of any passes that modify the ops. + if (inst.src2 >= 32) { + regs_.SetGPRImm(inst.dest, 0); + } else if (inst.src2 == 0) { + if (inst.dest != inst.src1) { + regs_.Map(inst); + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + } + } else { + regs_.Map(inst); + if (inst.dest != inst.src1) + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + SHL(32, regs_.R(inst.dest), Imm8(inst.src2)); + } + break; + + case IROp::ShrImm: + // Shouldn't happen, but let's be safe of any passes that modify the ops. + if (inst.src2 >= 32) { + regs_.SetGPRImm(inst.dest, 0); + } else if (inst.src2 == 0) { + if (inst.dest != inst.src1) { + regs_.Map(inst); + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + } + } else { + regs_.Map(inst); + if (inst.dest != inst.src1) + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + SHR(32, regs_.R(inst.dest), Imm8(inst.src2)); + } + break; + + case IROp::SarImm: + // Shouldn't happen, but let's be safe of any passes that modify the ops. + if (inst.src2 >= 32) { + regs_.Map(inst); + if (inst.dest != inst.src1) + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + SAR(32, regs_.R(inst.dest), Imm8(31)); + } else if (inst.src2 == 0) { + if (inst.dest != inst.src1) { + regs_.Map(inst); + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + } + } else { + regs_.Map(inst); + if (inst.dest != inst.src1) + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + SAR(32, regs_.R(inst.dest), Imm8(inst.src2)); + } + break; + + case IROp::RorImm: + if (inst.src2 == 0) { + if (inst.dest != inst.src1) { + regs_.Map(inst); + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + } + } else { + regs_.Map(inst); + if (inst.dest != inst.src1) + MOV(32, regs_.R(inst.dest), regs_.R(inst.src1)); + ROR(32, regs_.R(inst.dest), Imm8(inst.src2 & 31)); + } + break; + default: INVALIDOP; break; From 3c61f4f5cef905bc3a52e7e05944147745dedf66 Mon Sep 17 00:00:00 2001 From: DDinghoya Date: Mon, 21 Aug 2023 22:53:17 +0900 Subject: [PATCH 26/36] Update ko_KR.ini --- assets/lang/ko_KR.ini | 88 +++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/assets/lang/ko_KR.ini b/assets/lang/ko_KR.ini index 209e768038..08dc52e957 100644 --- a/assets/lang/ko_KR.ini +++ b/assets/lang/ko_KR.ini @@ -2,14 +2,14 @@ %d achievements, %d points = %d 성과, %d 포인트 %1: Attempt started = %1: 순위표 시도 시작 %1: Attempt failed = %1: 순위표 시도 실패 -#%1: Submitting leaderboard score: %2! = %1: 리더보드 점수 제출: %2! Account = 계정 -Achievement progress = 성과 진행률 Achievement unlocked = 성과 잠금 해제 +Achievement progress = 성과 진행률 Achievements = 성과 -Achievements are disabled = 성과 비활성화 Achievements enabled = 성과 활성화 -Around me = 내 주변 +Achievements are disabled = 성과 비활성화 +Achievements with active challenges = 도전 과제가 활성화된 성과 +Almost completed achievements = 거의 완료된 성과 Can't log in to RetroAchievements right now = 지금 RetroAchievements에 로그인할 수 없음 Challenge indicator = 도전 표시기 Challenge Mode = 도전 모드 @@ -25,28 +25,31 @@ Leaderboard attempt started or failed = 순위표 시도 시작 또는 실패 Leaderboard result submitted = 순위표 결과 제출 Leaderboard score submission = 순위표 점수 제출 Leaderboard submission is enabled = 순위표 제출 활성화 -Leaderboard tracker = 순위표 추적기 Leaderboards = 순위표 +Leaderboard tracker = 순위표 추적기 Links = 링크 Locked achievements = 잠겨진 성과 Log bad memory accesses = 불량 메모리 접속 로그 Mastered %1 = 마스터한 %1 +Around me = 내 주변 Notifications = 알림 -Submitted %1 for %2 = 2에 %1을(를) 제출 -This feature is not available in Challenge Mode = 도전 모드에서는 이 기능을 사용할 수 없음 -Save states not available in Challenge Mode = 도전 모드에서 사용할 수 없는 저장 상태 +Recently unlocked achievements = 최근 잠금 해제된 성과 Register on www.retroachievements.org = www.retroachievements.org에 등록 RetroAchievements are not available for this game = 이 게임에서는 RetroAchievements 사용할 수 없음 RetroAchievements website = RetroAchievements 웹사이트 Rich Presence = 활동 상태 +Save states not available in Challenge Mode = 도전 모드에서 사용할 수 없는 저장 상태 Save state loaded without achievement data = 성과 데이터 없이 로드된 상태 저장 Sound Effects = 사운드 효과 Statistics = 통계 +Submitted %1 for %2 = 2에 %1을(를) 제출함 Syncing achievements data... = 성과 데이터 동기화 중... Test Mode = 테스트 모드 +This feature is not available in Challenge Mode = 도전 모드에서는 이 기능을 사용할 수 없음 This game has no achievements = 이 게임에는 성과가 없음 -Top players = Top players +Top players = 상위 플레이어 Unlocked achievements = 잠금 해제된 성과 +Unsupported achievements = 지원되지 않는 성과 Unofficial achievements = 비공식 성과 [Audio] @@ -102,7 +105,7 @@ Disable diagonal input = 대각선 입력 비활성화 Double tap = 두 번 탭 Enable gesture control = 제스처 제어 활성화 Enable standard shortcut keys = 표준 단축키 활성화 -frames = frames +frames = 프레임 Gesture = 제스처 Gesture mapping = 제스처 맵핑 Glowing borders = 광선 테두리 @@ -147,7 +150,7 @@ Tilt control setup = 기울기 제어 설정... Tilt Input Type = 기울기 입력 유형 Tilt Sensitivity along X axis = X 축 기울기 감도 Tilt Sensitivity along Y axis = Y 축 기울기 감도 -To Calibrate = 원하는 각도로 장치를 잡고 "보정"을 누르십시오. +To Calibrate = 원하는 각도로 장치를 잡고 "보정"을 누르세요. Toggle mode = 토글 모드 Repeat mode = 반복 모드 Touch Control Visibility = 터치 제어 가시성 @@ -220,7 +223,7 @@ Log Console = 콘솔 로그(&L) Memory View... = 메모리 보기(&V)... More Settings... = 기타 설정(&M)... Nearest = 근접 필터링(&N) -Skip Buffer Effects = 버퍼 효과 건너뛰기(버퍼되지 않음, 더 빠름)(&S) +Skip Buffer Effects = 버퍼 효과 건너뛰기(&S) Off = 끔(&O) Open Chat = 채팅 열기 Open Directory... = 디렉터리 열기(&D)... @@ -269,9 +272,11 @@ xBRZ = xBRZ 필터(&X) [Developer] Allocator Viewer = 할당기 뷰어 (Vulkan) Allow remote debugger = 원격 디버거 허용 +Audio Debug = 오디오 디버그 Backspace = 백스페이스 Block address = 주소 차단 By Address = 주소별 +Control Debug = 디버그 제어 Copy savestates to memstick root = 메모리 스틱 루트에 저장 상태 복사 Create/Open textures.ini file for current game = 현재 게임에 대한 textures.ini 파일 생성/열기 Current = 현재 @@ -284,9 +289,11 @@ Dump next frame to log = 다음 프레임을 로그로 덤프 Enable driver bug workarounds = 드라이버 버그 해결 방법 활성화 Enable Logging = 디버그 로깅 활성화 Enter address = 주소 입력 +Fragment = 단편 FPU = FPU Framedump tests = 프레임 덤프 테스트 Frame Profiler = 프레임 프로파일러 +Frame timing = 프레임 타이밍 GPU Driver Test = GPU 드라이버 테스트 GPU Profile = GPU 프로파일 GPU log profiler = GPU 로그 프로파일러 @@ -316,10 +323,10 @@ Stats = 상태 System Information = 시스템 정보 Texture ini file created = 텍스처 ini 파일 생성 Texture Replacement = 텍스쳐 교체 -Audio Debug = 오디오 디버그 -Control Debug = 제어 디버그 토글 Toggle Freeze = 프리징 토글 Touchscreen Test = 터치화면 테스트 +Ubershaders = 우버셰이더 +Vertex = Vertex VFPU = VFPU [Dialog] @@ -334,8 +341,8 @@ Cancel = 취소 Center = 중앙 Center Left = 중앙 왼쪽 Center Right = 중앙 오른쪽 -Changing this setting requires PPSSPP to restart. = 이 설정을 변경하려면 PPSSPP를 다시 시작해야 합니다. Channel: = 채널: +Changing this setting requires PPSSPP to restart. = 이 설정을 변경하려면 PPSSPP를 다시 시작해야 합니다. Choose PPSSPP save folder = PPSSPP 저장 폴더 선택 Confirm Overwrite = 데이터를 덮어쓰겠습니까? Confirm Save = 이 데이터를 저장할까요? @@ -360,13 +367,14 @@ Edit = 편집 Enable All = 모두 활성화 Enabled = 활성화 Enter = 입력 -Failed to connect to server, check your internet connection. = 서버에 연결하지 못했습니다. 인터넷 연결을 확인하세요. Failed to log in, check your username and password. = 로그인에 실패했습니다. 사용자 이름과 비밀번호를 확인하세요. +Failed to connect to server, check your internet connection. = 서버에 연결하지 못했습니다. 인터넷 연결을 확인하세요. Filter = 필터 Finish = 마침 GE Frame Dumps = GE 프레임 덤프 Grid = 그리드 Inactive = 비활성 +Installing... = 설치 중... InternalError = 내부 오류가 발생했습니다. Load = 불러오기 Load completed = 불러오기가 완료되었습니다. @@ -374,8 +382,8 @@ Loading = 불러오기 중\n잠시만 기다려 주세요... LoadingFailed = 데이터를 불러올 수 없습니다. Log in = 로그인 Log out = 로그아웃 -Logged in! = 로그인했습니다! Logging in... = 로그인 중... +Logged in! = 로그인했습니다! Move = 이동 Move Up = 위로 이동 Move Down = 아래로 이동 @@ -646,7 +654,7 @@ VertexCache Tip = 더 빠르지만 일시적인 깜박임이 발생할 수 있 VSync = 수직 동기화 Vulkan = Vulkan Window Size = 창 크기 -xBRZ = xBRZ 필터 +xBRZ = xBRZ [InstallZip] Delete ZIP file = ZIP 파일 삭제 @@ -754,8 +762,8 @@ RightAn.Down = 우측 아날로그 아래쪽 RightAn.Left = 우측 아날로그 왼쪽 RightAn.Right = 우측 아날로그 오른쪽 RightAn.Up = 우측 아날로그 위쪽 -Rotate Analog (CCW) = 아날로그 회전 (CCW) -Rotate Analog (CW) = 아날로그 회전 (CW) +Rotate Analog (CCW) = 아날로그 회전 (반시계방향) +Rotate Analog (CW) = 아날로그 회전 (시계방향) Save State = 상태 저장하기 Screen = 화면 Screenshot = 스크린샷 @@ -843,23 +851,6 @@ Validating address... = 주소 검증하는 중... WLAN Channel = 무선랜 채널 You're in Offline Mode, go to lobby or online hall = 오프라인 모드입니다. 로비 또는 온라인 홀으로 이동합니다. -[PSPSettings] -Auto = Auto -Chinese (simplified) = 중국어 (간체) -Chinese (traditional) = 중국어 (번체) -Dutch = 네덜란드어 -English = 영어 -French = 프랑스어 -Game language = 게임 언어 -German = 독일어 -Italian = 이탈리아어 -Japanese = 일본어 -Korean = 한국어 -Games often don't support all languages = 게임이 모든 언어를 지원하지 않는 경우가 많음 -Portuguese = 포르투갈어 -Russian = 러시아어 -Spanish = 스페인어 - [Pause] Cheats = 치트 Continue = 계속 @@ -892,10 +883,10 @@ ColorCorrection = 색상 교정 ColorPreservation = 색상 보존 Contrast = 명암 CRT = CRT 스캔라인 -FakeReflections = 가짜반사 FXAA = FXAA 안티 에일리어싱 Gamma = 감마 GreenLevel = 녹색 수준 +FakeReflections = 가짜반사 Intensity = 강도 LCDPersistence = LCD 지속성 Natural = 자연스러운 색상 @@ -944,7 +935,7 @@ specialthanks = 특별히 감사한 분: specialthanksKeithGalocy = NVIDIA (하드웨어, 조언) specialthanksMaxim = 놀라운 Atrac3+ 디코더 작업 testing = 테스트 -this translation by = 번역자: +this translation by = 번역가: title = 빠르고 휴대 가능한 PSP 에뮬레이터 tools = 사용된 무료 도구: # Add translators or contributors who translated PPSSPP into your language here. @@ -989,6 +980,23 @@ Use PSP folder at root of storage = 저장공간 루트에서 PSP 폴더 사용 Welcome to PPSSPP! = PPSSPP에 오신 것을 환영합니다! WhatsThis = 이것은 무엇인가요? +[PSPSettings] +Auto = 자동 +Chinese (simplified) = 중국어 (간체) +Chinese (traditional) = 중국어 (번체) +Dutch = 네덜란드어 +English = 영어 +French = 프랑스어 +Game language = 게임 언어 +German = 독일어 +Italian = 이탈리아어 +Japanese = 일본어 +Korean = 한국어 +Games often don't support all languages = 게임이 모든 언어를 지원하지 않는 경우가 많음 +Portuguese = 포르투갈어 +Russian = 러시아어 +Spanish = 스페인어 + [RemoteISO] Browse Games = 게임 찾아보기 Local Server Port = 로컬 서버 포트 @@ -1139,7 +1147,7 @@ GPU Flags = GPU 플래그 GPU Information = GPU 정보 High precision float range = 고정밀 부동소수점 범위 High precision int range = 고정밀 정수 범위 -Icon cache = Icon cache +Icon cache = 아이콘 캐시 JIT available = JIT 사용 가능 Lang/Region = 언어/지역 Memory Page Size = 메모리 페이지 크기 From 40b3ff95737e18ea7a0890e279384467d68d769b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 21 Aug 2023 08:23:58 -0700 Subject: [PATCH 27/36] x86jit: Fix spill issue. --- Core/MIPS/x86/X64IRCompALU.cpp | 1 + Core/MIPS/x86/X64IRRegCache.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Core/MIPS/x86/X64IRCompALU.cpp b/Core/MIPS/x86/X64IRCompALU.cpp index 7809a217b4..daee4db18b 100644 --- a/Core/MIPS/x86/X64IRCompALU.cpp +++ b/Core/MIPS/x86/X64IRCompALU.cpp @@ -64,6 +64,7 @@ void X64JitBackend::CompIR_Arith(IRInst inst) { if (regs_.IsGPRImm(inst.src1) && regs_.GetGPRImm(inst.src1) == 0) { // TODO: Might be nice to have a pass to turn this into Neg. // Special cased to avoid wasting a reg on zero. + regs_.SpillLockGPR(inst.dest, inst.src2); regs_.MapGPR(inst.src2); regs_.MapGPR(inst.dest, MIPSMap::NOINIT); if (inst.dest != inst.src2) diff --git a/Core/MIPS/x86/X64IRRegCache.cpp b/Core/MIPS/x86/X64IRRegCache.cpp index 17a03d6292..cb47994397 100644 --- a/Core/MIPS/x86/X64IRRegCache.cpp +++ b/Core/MIPS/x86/X64IRRegCache.cpp @@ -61,7 +61,7 @@ const int *X64IRRegCache::GetAllocationOrder(MIPSLoc type, int &count, int &base // Intentionally last. R15, #elif PPSSPP_ARCH(X86) - ESI, EDI, EDX, ECX, EBX, + ESI, EDI, EDX, EBX, ECX, #endif }; @@ -79,9 +79,9 @@ const int *X64IRRegCache::GetAllocationOrder(MIPSLoc type, int &count, int &base // TODO: Might have to change this if we can't live without dedicated temps. static const int allocationOrder[] = { #if PPSSPP_ARCH(AMD64) - XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, XMM0, XMM1, XMM2, XMM3, XMM4, XMM5 + XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, XMM1, XMM2, XMM3, XMM4, XMM5, XMM0, #elif PPSSPP_ARCH(X86) - XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, + XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM0, #endif }; From 1ccc2d5d2f7a6c3876cec467095481fd55ce4466 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 21 Aug 2023 08:24:25 -0700 Subject: [PATCH 28/36] x86jit: Fix address offsets with memview mask. --- Core/MIPS/x86/X64IRAsm.cpp | 2 +- Core/MIPS/x86/X64IRCompLoadStore.cpp | 72 +++++++++++++++------------- Core/MIPS/x86/X64IRJit.h | 2 + 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/Core/MIPS/x86/X64IRAsm.cpp b/Core/MIPS/x86/X64IRAsm.cpp index b9a24a5e05..6c66a38eb1 100644 --- a/Core/MIPS/x86/X64IRAsm.cpp +++ b/Core/MIPS/x86/X64IRAsm.cpp @@ -176,7 +176,7 @@ void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) { else ABI_CallFunctionAC(reinterpret_cast(&ShowPC), R(MEMBASEREG), (u32)jitbase); #else - ABI_CallFunctionCC(reinterpret_cast(&ShowPC), Menory::base, (u32)jitbase); + ABI_CallFunctionCC(reinterpret_cast(&ShowPC), (u32)Memory::base, (u32)GetBasePtr()); #endif } diff --git a/Core/MIPS/x86/X64IRCompLoadStore.cpp b/Core/MIPS/x86/X64IRCompLoadStore.cpp index 1576c63706..132c3f8575 100644 --- a/Core/MIPS/x86/X64IRCompLoadStore.cpp +++ b/Core/MIPS/x86/X64IRCompLoadStore.cpp @@ -37,6 +37,42 @@ namespace MIPSComp { using namespace Gen; using namespace X64IRJitConstants; +Gen::OpArg X64JitBackend::PrepareSrc1Address(IRInst inst) { + const IRMeta *m = GetIRMeta(inst.op); + bool readsFromSrc1 = inst.src1 == inst.src3 && (m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0; + + OpArg addrArg; + if (inst.src1 == MIPS_REG_ZERO) { +#ifdef MASKED_PSP_MEMORY + inst.constant &= Memory::MEMVIEW32_MASK; +#endif +#if PPSSPP_ARCH(AMD64) + addrArg = MDisp(MEMBASEREG, inst.constant & 0x7FFFFFFF); +#else + addrArg = M(Memory::base + inst.constant); +#endif + } else if ((jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) && !readsFromSrc1) { + X64Reg src1 = regs_.MapGPRAsPointer(inst.src1); + addrArg = MDisp(src1, (int)inst.constant); + } else { + regs_.MapGPR(inst.src1); +#ifdef MASKED_PSP_MEMORY + LEA(PTRBITS, SCRATCH1, MDisp(regs_.RX(inst.src1), (int)inst.constant)); + AND(PTRBITS, R(SCRATCH1), Imm32(Memory::MEMVIEW32_MASK)); + ADD(PTRBITS, R(SCRATCH1), ImmPtr(Memory::base)); + addrArg = MatR(SCRATCH1); +#else +#if PPSSPP_ARCH(AMD64) + addrArg = MComplex(MEMBASEREG, regs_.RX(inst.src1), SCALE_1, (int)inst.constant); +#else + addrArg = MDisp(regs_.RX(inst.src1), Memory::base + inst.constant); +#endif +#endif + } + + return addrArg; +} + void X64JitBackend::CompIR_CondStore(IRInst inst) { CONDITIONAL_DISABLE; @@ -83,23 +119,7 @@ void X64JitBackend::CompIR_Load(IRInst inst) { CONDITIONAL_DISABLE; regs_.SpillLockGPR(inst.dest, inst.src1); - OpArg addrArg; - if (inst.src1 == MIPS_REG_ZERO) { -#ifdef MASKED_PSP_MEMORY - inst.constant &= Memory::MEMVIEW32_MASK; -#endif -#if PPSSPP_ARCH(AMD64) - addrArg = MDisp(MEMBASEREG, inst.constant & 0x7FFFFFFF); -#else - addrArg = M(Memory::base + inst.constant); -#endif - } else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) { - X64Reg src1 = regs_.MapGPRAsPointer(inst.src1); - addrArg = MDisp(src1, inst.constant & 0x7FFFFFFF); - } else { - regs_.MapGPR(inst.src1); - addrArg = MComplex(MEMBASEREG, regs_.RX(inst.src1), SCALE_1, inst.constant & 0x7FFFFFFF); - } + OpArg addrArg = PrepareSrc1Address(inst); // With NOINIT, MapReg won't subtract MEMBASEREG even if dest == src1. regs_.MapGPR(inst.dest, MIPSMap::NOINIT); @@ -158,23 +178,7 @@ void X64JitBackend::CompIR_Store(IRInst inst) { CONDITIONAL_DISABLE; regs_.SpillLockGPR(inst.src3, inst.src1); - OpArg addrArg; - if (inst.src1 == MIPS_REG_ZERO) { -#ifdef MASKED_PSP_MEMORY - inst.constant &= Memory::MEMVIEW32_MASK; -#endif -#if PPSSPP_ARCH(AMD64) - addrArg = MDisp(MEMBASEREG, inst.constant & 0x7FFFFFFF); -#else - addrArg = M(Memory::base + inst.constant); -#endif - } else if ((jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) { - X64Reg src1 = regs_.MapGPRAsPointer(inst.src1); - addrArg = MDisp(src1, inst.constant & 0x7FFFFFFF); - } else { - regs_.MapGPR(inst.src1); - addrArg = MComplex(MEMBASEREG, regs_.RX(inst.src1), SCALE_1, inst.constant & 0x7FFFFFFF); - } + OpArg addrArg = PrepareSrc1Address(inst); OpArg valueArg; X64Reg valueReg = regs_.TryMapTempImm(inst.src3); diff --git a/Core/MIPS/x86/X64IRJit.h b/Core/MIPS/x86/X64IRJit.h index afccf2bd5d..3b5189398e 100644 --- a/Core/MIPS/x86/X64IRJit.h +++ b/Core/MIPS/x86/X64IRJit.h @@ -106,6 +106,8 @@ private: void CompIR_VecStore(IRInst inst) override; void CompIR_ValidateAddress(IRInst inst) override; + Gen::OpArg PrepareSrc1Address(IRInst inst); + JitOptions &jo; X64IRRegCache regs_; From 3c66523f044e9d7890658b89574029d8e039f869 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Tue, 22 Aug 2023 04:29:07 +0700 Subject: [PATCH 29/36] A quick fix for Metal Gear Acid due to adhocctl's busy state never reset to false when there are no adhocctl's handler. --- Core/HLE/sceNetAdhoc.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 6ab81d8f05..ad1363cf7a 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -2540,12 +2540,8 @@ u32 NetAdhocctl_Disconnect() { adhocctlCurrentMode = ADHOCCTL_MODE_NONE; // Notify Event Handlers (even if we weren't connected, not doing this will freeze games like God Eater, which expect this behaviour) // FIXME: When there are no handler the state will immediately became ADHOCCTL_STATE_DISCONNECTED ? - if (adhocctlHandlers.empty()) { - adhocctlState = ADHOCCTL_STATE_DISCONNECTED; - } - else { - notifyAdhocctlHandlers(ADHOCCTL_EVENT_DISCONNECT, 0); - } + // Note: Metal Gear Acid [2] never register a handler until it's successfully connected to a group and have a connected socket to other player, thus adhocctlHandlers is always empty here. + notifyAdhocctlHandlers(ADHOCCTL_EVENT_DISCONNECT, 0); // Return Success, some games might ignore returned value and always treat it as success, otherwise repeatedly calling this function return 0; From bea74ba162994ed26500415392cdcb334e8cbe07 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 21 Aug 2023 08:25:25 -0700 Subject: [PATCH 30/36] x86jit: Avoid negative offset warning. --- Core/MIPS/x86/X64IRAsm.cpp | 28 ++++++++++++++-------------- Core/MIPS/x86/X64IRCompSystem.cpp | 2 +- Core/MIPS/x86/X64IRJit.cpp | 12 ++++++------ Core/MIPS/x86/X64IRRegCache.h | 5 +++++ 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/Core/MIPS/x86/X64IRAsm.cpp b/Core/MIPS/x86/X64IRAsm.cpp index 6c66a38eb1..7282bc68d4 100644 --- a/Core/MIPS/x86/X64IRAsm.cpp +++ b/Core/MIPS/x86/X64IRAsm.cpp @@ -54,13 +54,13 @@ void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) { if (jo.useStaticAlloc && false) { saveStaticRegisters_ = AlignCode16(); - //MOV(32, MDisp(CTXREG, -128 + offsetof(MIPSState, downcount)), R(DOWNCOUNTREG)); + //MOV(32, MDisp(CTXREG, downcountOffset), R(DOWNCOUNTREG)); //regs_.EmitSaveStaticRegisters(); RET(); loadStaticRegisters_ = AlignCode16(); //regs_.EmitLoadStaticRegisters(); - //MOV(32, R(DOWNCOUNTREG), MDisp(CTXREG, -128 + offsetof(MIPSState, downcount))); + //MOV(32, R(DOWNCOUNTREG), MDisp(CTXREG, downcountOffset)); RET(); start = saveStaticRegisters_; @@ -71,22 +71,22 @@ void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) { restoreRoundingMode_ = AlignCode16(); { - STMXCSR(MDisp(CTXREG, -128 + offsetof(MIPSState, temp))); + STMXCSR(MDisp(CTXREG, tempOffset)); // Clear the rounding mode and flush-to-zero bits back to 0. - AND(32, MDisp(CTXREG, -128 + offsetof(MIPSState, temp)), Imm32(~(7 << 13))); - LDMXCSR(MDisp(CTXREG, -128 + offsetof(MIPSState, temp))); + AND(32, MDisp(CTXREG, tempOffset), Imm32(~(7 << 13))); + LDMXCSR(MDisp(CTXREG, tempOffset)); RET(); } applyRoundingMode_ = AlignCode16(); { - MOV(32, R(SCRATCH1), MDisp(CTXREG, -128 + offsetof(MIPSState, fcr31))); + MOV(32, R(SCRATCH1), MDisp(CTXREG, fcr31Offset)); AND(32, R(SCRATCH1), Imm32(0x01000003)); // If it's 0 (nearest + no flush0), we don't actually bother setting - we cleared the rounding // mode out in restoreRoundingMode anyway. This is the most common. FixupBranch skip = J_CC(CC_Z); - STMXCSR(MDisp(CTXREG, -128 + offsetof(MIPSState, temp))); + STMXCSR(MDisp(CTXREG, tempOffset)); // The MIPS bits don't correspond exactly, so we have to adjust. // 0 -> 0 (skip2), 1 -> 3, 2 -> 2 (skip2), 3 -> 1 @@ -99,15 +99,15 @@ void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) { SHL(32, R(SCRATCH1), Imm8(13)); // Before setting new bits, we must clear the old ones. // Clearing bits 13-14 (rounding mode) and 15 (flush to zero.) - AND(32, MDisp(CTXREG, -128 + offsetof(MIPSState, temp)), Imm32(~(7 << 13))); - OR(32, MDisp(CTXREG, -128 + offsetof(MIPSState, temp)), R(SCRATCH1)); + AND(32, MDisp(CTXREG, tempOffset), Imm32(~(7 << 13))); + OR(32, MDisp(CTXREG, tempOffset), R(SCRATCH1)); - TEST(32, MDisp(CTXREG, -128 + offsetof(MIPSState, fcr31)), Imm32(1 << 24)); + TEST(32, MDisp(CTXREG, fcr31Offset), Imm32(1 << 24)); FixupBranch skip3 = J_CC(CC_Z); - OR(32, MDisp(CTXREG, -128 + offsetof(MIPSState, temp)), Imm32(1 << 15)); + OR(32, MDisp(CTXREG, tempOffset), Imm32(1 << 15)); SetJumpTarget(skip3); - LDMXCSR(MDisp(CTXREG, -128 + offsetof(MIPSState, temp))); + LDMXCSR(MDisp(CTXREG, tempOffset)); SetJumpTarget(skip); RET(); } @@ -151,7 +151,7 @@ void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) { FixupBranch badCoreState = J_CC(CC_NZ, true); //CMP(32, R(DOWNCOUNTREG), Imm32(0)); - CMP(32, MDisp(CTXREG, -128 + offsetof(MIPSState, downcount)), Imm32(0)); + CMP(32, MDisp(CTXREG, downcountOffset), Imm32(0)); J_CC(CC_S, outerLoop_); FixupBranch skipToRealDispatch = J(); @@ -162,7 +162,7 @@ void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) { // TODO: See if we can get the slice decrement to line up with IR. //CMP(32, R(DOWNCOUNTREG), Imm32(0)); - CMP(32, MDisp(CTXREG, -128 + offsetof(MIPSState, downcount)), Imm32(0)); + CMP(32, MDisp(CTXREG, downcountOffset), Imm32(0)); FixupBranch bail = J_CC(CC_S, true); SetJumpTarget(skipToRealDispatch); diff --git a/Core/MIPS/x86/X64IRCompSystem.cpp b/Core/MIPS/x86/X64IRCompSystem.cpp index 5ff96d0c86..7e1061f494 100644 --- a/Core/MIPS/x86/X64IRCompSystem.cpp +++ b/Core/MIPS/x86/X64IRCompSystem.cpp @@ -47,7 +47,7 @@ void X64JitBackend::CompIR_Basic(IRInst inst) { switch (inst.op) { case IROp::Downcount: //ADD(32, R(DOWNCOUNTREG), R(DOWNCOUNTREG), Imm32(-(s32)inst.constant)); - SUB(32, MDisp(CTXREG, -128 + offsetof(MIPSState, downcount)), Imm32((s32)inst.constant)); + SUB(32, MDisp(CTXREG, downcountOffset), Imm32((s32)inst.constant)); break; case IROp::SetConst: diff --git a/Core/MIPS/x86/X64IRJit.cpp b/Core/MIPS/x86/X64IRJit.cpp index 3cb57d5b2c..6f3d7e24d1 100644 --- a/Core/MIPS/x86/X64IRJit.cpp +++ b/Core/MIPS/x86/X64IRJit.cpp @@ -64,7 +64,7 @@ bool X64JitBackend::CompileBlock(IRBlock *block, int block_num, bool preload) { // TODO: See if we can get flags to always have the downcount compare. //CMP(32, R(DOWNCOUNTREG), Imm32(0)); - CMP(32, MDisp(CTXREG, -128 + offsetof(MIPSState, downcount)), Imm32(0)); + CMP(32, MDisp(CTXREG, downcountOffset), Imm32(0)); FixupBranch normalEntry = J_CC(CC_NS); MOV(32, R(SCRATCH1), Imm32(startPC)); JMP(outerLoopPCInSCRATCH1_, true); @@ -117,7 +117,7 @@ bool X64JitBackend::CompileBlock(IRBlock *block, int block_num, bool preload) { if (jo.enableBlocklink && jo.useBackJump) { //CMP(32, R(DOWNCOUNTREG), Imm32(0)); - CMP(32, MDisp(CTXREG, -128 + offsetof(MIPSState, downcount)), Imm32(0)); + CMP(32, MDisp(CTXREG, downcountOffset), Imm32(0)); J_CC(CC_NS, blockStart, true); MOV(32, R(SCRATCH1), Imm32(startPC)); @@ -302,11 +302,11 @@ void X64JitBackend::ApplyRoundingMode(bool force) { } void X64JitBackend::MovFromPC(X64Reg r) { - MOV(32, R(r), MDisp(CTXREG, -128 + offsetof(MIPSState, pc))); + MOV(32, R(r), MDisp(CTXREG, pcOffset)); } void X64JitBackend::MovToPC(X64Reg r) { - MOV(32, MDisp(CTXREG, -128 + offsetof(MIPSState, pc)), R(r)); + MOV(32, MDisp(CTXREG, pcOffset), R(r)); } void X64JitBackend::SaveStaticRegisters() { @@ -314,7 +314,7 @@ void X64JitBackend::SaveStaticRegisters() { //CALL(saveStaticRegisters_); } else { // Inline the single operation - //MOV(32, MDisp(CTXREG, -128 + offsetof(MIPSState, downcount)), R(DOWNCOUNTREG)); + //MOV(32, MDisp(CTXREG, downcountOffset), R(DOWNCOUNTREG)); } } @@ -322,7 +322,7 @@ void X64JitBackend::LoadStaticRegisters() { if (jo.useStaticAlloc) { //CALL(loadStaticRegisters_); } else { - //MOV(32, R(DOWNCOUNTREG), MDisp(CTXREG, -128 + offsetof(MIPSState, downcount))); + //MOV(32, R(DOWNCOUNTREG), MDisp(CTXREG, downcountOffset)); } } diff --git a/Core/MIPS/x86/X64IRRegCache.h b/Core/MIPS/x86/X64IRRegCache.h index 036fc4fbe5..a56dffb3a7 100644 --- a/Core/MIPS/x86/X64IRRegCache.h +++ b/Core/MIPS/x86/X64IRRegCache.h @@ -36,6 +36,11 @@ const Gen::X64Reg CTXREG = Gen::EBP; #endif const Gen::X64Reg SCRATCH1 = Gen::EAX; +static constexpr auto downcountOffset = offsetof(MIPSState, downcount) - 128; +static constexpr auto tempOffset = offsetof(MIPSState, temp) - 128; +static constexpr auto fcr31Offset = offsetof(MIPSState, fcr31) - 128; +static constexpr auto pcOffset = offsetof(MIPSState, pc) - 128; + } // namespace X64IRJitConstants class X64IRRegCache : public IRNativeRegCacheBase { From db34b8510767ff11d03dbfb2572045093d01b522 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 21 Aug 2023 20:46:05 -0700 Subject: [PATCH 31/36] irjit: Allow flag-based allocation order. Sometimes backends have needs, like XMM0/v0-only, or similar. --- Core/MIPS/IR/IRRegCache.cpp | 28 ++++++++++++++-------------- Core/MIPS/IR/IRRegCache.h | 9 +++++---- Core/MIPS/RiscV/RiscVRegCache.cpp | 4 ++-- Core/MIPS/RiscV/RiscVRegCache.h | 2 +- Core/MIPS/x86/X64IRRegCache.cpp | 4 ++-- Core/MIPS/x86/X64IRRegCache.h | 2 +- 6 files changed, 25 insertions(+), 24 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index 985adad7a2..0141041500 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -330,18 +330,18 @@ void IRNativeRegCacheBase::MarkGPRAsPointerDirty(IRReg gpr) { // Stays pointerified or REG_AS_PTR. } -IRNativeReg IRNativeRegCacheBase::AllocateReg(MIPSLoc type) { +IRNativeReg IRNativeRegCacheBase::AllocateReg(MIPSLoc type, MIPSMap flags) { _dbg_assert_(type == MIPSLoc::REG || type == MIPSLoc::FREG || type == MIPSLoc::VREG); - IRNativeReg nreg = FindFreeReg(type); + IRNativeReg nreg = FindFreeReg(type, flags); if (nreg != -1) return nreg; // Still nothing. Let's spill a reg and goto 10. bool clobbered; - IRNativeReg bestToSpill = FindBestToSpill(type, true, &clobbered); + IRNativeReg bestToSpill = FindBestToSpill(type, flags, true, &clobbered); if (bestToSpill == -1) { - bestToSpill = FindBestToSpill(type, false, &clobbered); + bestToSpill = FindBestToSpill(type, flags, false, &clobbered); } if (bestToSpill != -1) { @@ -351,7 +351,7 @@ IRNativeReg IRNativeRegCacheBase::AllocateReg(MIPSLoc type) { FlushNativeReg(bestToSpill); } // Now one must be free. - return FindFreeReg(type); + return FindFreeReg(type, flags); } // Uh oh, we have all of them spilllocked.... @@ -360,9 +360,9 @@ IRNativeReg IRNativeRegCacheBase::AllocateReg(MIPSLoc type) { return -1; } -IRNativeReg IRNativeRegCacheBase::FindFreeReg(MIPSLoc type) const { +IRNativeReg IRNativeRegCacheBase::FindFreeReg(MIPSLoc type, MIPSMap flags) const { int allocCount = 0, base = 0; - const int *allocOrder = GetAllocationOrder(type, allocCount, base); + const int *allocOrder = GetAllocationOrder(type, flags, allocCount, base); for (int i = 0; i < allocCount; i++) { IRNativeReg nreg = IRNativeReg(allocOrder[i] - base); @@ -375,9 +375,9 @@ IRNativeReg IRNativeRegCacheBase::FindFreeReg(MIPSLoc type) const { return -1; } -IRNativeReg IRNativeRegCacheBase::FindBestToSpill(MIPSLoc type, bool unusedOnly, bool *clobbered) const { +IRNativeReg IRNativeRegCacheBase::FindBestToSpill(MIPSLoc type, MIPSMap flags, bool unusedOnly, bool *clobbered) const { int allocCount = 0, base = 0; - const int *allocOrder = GetAllocationOrder(type, allocCount, base); + const int *allocOrder = GetAllocationOrder(type, flags, allocCount, base); static const int UNUSED_LOOKAHEAD_OPS = 30; @@ -670,7 +670,7 @@ IRNativeReg IRNativeRegCacheBase::MapWithTemp(const IRInst &inst, MIPSLoc type) ApplyMapping(mapping, 3); // Grab a temp while things are spill locked. - IRNativeReg temp = AllocateReg(type); + IRNativeReg temp = AllocateReg(type, MIPSMap::INIT); CleanupMapping(mapping, 3); return temp; } @@ -829,22 +829,22 @@ IRNativeReg IRNativeRegCacheBase::MapNativeReg(MIPSLoc type, IRReg first, int la case MIPSLoc::REG_AS_PTR: case MIPSLoc::REG: if (type != MIPSLoc::REG) - nreg = AllocateReg(type); + nreg = AllocateReg(type, flags); break; case MIPSLoc::FREG: if (type != MIPSLoc::FREG) - nreg = AllocateReg(type); + nreg = AllocateReg(type, flags); break; case MIPSLoc::VREG: if (type != MIPSLoc::VREG) - nreg = AllocateReg(type); + nreg = AllocateReg(type, flags); break; case MIPSLoc::IMM: case MIPSLoc::MEM: - nreg = AllocateReg(type); + nreg = AllocateReg(type, flags); break; } } diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index 23cfa32b51..2d162a835b 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -74,6 +74,7 @@ private: }; // Initing is the default so the flag is reversed. +// 8 bits - upper 4 are reserved for backend purposes. enum class MIPSMap : uint8_t { INIT = 0, DIRTY = 1, @@ -191,15 +192,15 @@ public: protected: virtual void SetupInitialRegs(); - virtual const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const = 0; + virtual const int *GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &count, int &base) const = 0; virtual const StaticAllocation *GetStaticAllocations(int &count) const { count = 0; return nullptr; } - IRNativeReg AllocateReg(MIPSLoc type); - IRNativeReg FindFreeReg(MIPSLoc type) const; - IRNativeReg FindBestToSpill(MIPSLoc type, bool unusedOnly, bool *clobbered) const; + IRNativeReg AllocateReg(MIPSLoc type, MIPSMap flags); + IRNativeReg FindFreeReg(MIPSLoc type, MIPSMap flags) const; + IRNativeReg FindBestToSpill(MIPSLoc type, MIPSMap flags, bool unusedOnly, bool *clobbered) const; virtual void DiscardNativeReg(IRNativeReg nreg); virtual void FlushNativeReg(IRNativeReg nreg); virtual void DiscardReg(IRReg mreg); diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index e7017ebd7b..a39dd6b344 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -55,7 +55,7 @@ void RiscVRegCache::SetupInitialRegs() { mrInitial_[MIPS_REG_ZERO].isStatic = true; } -const int *RiscVRegCache::GetAllocationOrder(MIPSLoc type, int &count, int &base) const { +const int *RiscVRegCache::GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &count, int &base) const { base = X0; if (type == MIPSLoc::REG) { @@ -236,7 +236,7 @@ RiscVReg RiscVRegCache::TryMapTempImm(IRReg r) { } RiscVReg RiscVRegCache::GetAndLockTempR() { - RiscVReg reg = (RiscVReg)AllocateReg(MIPSLoc::REG); + RiscVReg reg = (RiscVReg)AllocateReg(MIPSLoc::REG, MIPSMap::INIT); if (reg != INVALID_REG) { nr[reg].tempLockIRIndex = irIndex_; } diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index 4da57cc9b0..c1b140896f 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -73,7 +73,7 @@ public: protected: void SetupInitialRegs() override; const StaticAllocation *GetStaticAllocations(int &count) const override; - const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const override; + const int *GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &count, int &base) const override; void AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) override; void LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; diff --git a/Core/MIPS/x86/X64IRRegCache.cpp b/Core/MIPS/x86/X64IRRegCache.cpp index cb47994397..5b39b28de1 100644 --- a/Core/MIPS/x86/X64IRRegCache.cpp +++ b/Core/MIPS/x86/X64IRRegCache.cpp @@ -46,7 +46,7 @@ void X64IRRegCache::Init(XEmitter *emitter) { emit_ = emitter; } -const int *X64IRRegCache::GetAllocationOrder(MIPSLoc type, int &count, int &base) const { +const int *X64IRRegCache::GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &count, int &base) const { if (type == MIPSLoc::REG) { base = RAX; @@ -143,7 +143,7 @@ X64Reg X64IRRegCache::TryMapTempImm(IRReg r) { } X64Reg X64IRRegCache::GetAndLockTempR() { - X64Reg reg = FromNativeReg(AllocateReg(MIPSLoc::REG)); + X64Reg reg = FromNativeReg(AllocateReg(MIPSLoc::REG, MIPSMap::INIT)); if (reg != INVALID_REG) { nr[reg].tempLockIRIndex = irIndex_; } diff --git a/Core/MIPS/x86/X64IRRegCache.h b/Core/MIPS/x86/X64IRRegCache.h index a56dffb3a7..3e5909591b 100644 --- a/Core/MIPS/x86/X64IRRegCache.h +++ b/Core/MIPS/x86/X64IRRegCache.h @@ -71,7 +71,7 @@ public: Gen::X64Reg FX(IRReg preg); protected: - const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const override; + const int *GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &count, int &base) const override; void AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) override; void LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; From 55654f52b26281c736b9eb8726003a0999f030e9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 21 Aug 2023 21:03:14 -0700 Subject: [PATCH 32/36] x86jit: Fix Store8 on i386. Require an 8-bit capable register in mapping. --- Core/MIPS/x86/X64IRCompLoadStore.cpp | 7 ++++-- Core/MIPS/x86/X64IRRegCache.cpp | 37 +++++++++++++++++++++++++--- Core/MIPS/x86/X64IRRegCache.h | 21 +++++++++++++++- 3 files changed, 58 insertions(+), 7 deletions(-) diff --git a/Core/MIPS/x86/X64IRCompLoadStore.cpp b/Core/MIPS/x86/X64IRCompLoadStore.cpp index 132c3f8575..09f0269c7d 100644 --- a/Core/MIPS/x86/X64IRCompLoadStore.cpp +++ b/Core/MIPS/x86/X64IRCompLoadStore.cpp @@ -180,8 +180,11 @@ void X64JitBackend::CompIR_Store(IRInst inst) { regs_.SpillLockGPR(inst.src3, inst.src1); OpArg addrArg = PrepareSrc1Address(inst); + // i386 can only use certain regs for 8-bit operations. + X64Map valueFlags = inst.op == IROp::Store8 ? X64Map::LOW_SUBREG : X64Map::NONE; + OpArg valueArg; - X64Reg valueReg = regs_.TryMapTempImm(inst.src3); + X64Reg valueReg = regs_.TryMapTempImm(inst.src3, valueFlags); if (valueReg != INVALID_REG) { valueArg = R(valueReg); } else if (regs_.IsGPRImm(inst.src3)) { @@ -195,7 +198,7 @@ void X64JitBackend::CompIR_Store(IRInst inst) { break; } } else { - valueArg = R(regs_.MapGPR(inst.src3)); + valueArg = R(regs_.MapGPR(inst.src3, MIPSMap::INIT | valueFlags)); } // TODO: Safe memory? Or enough to have crash handler + validate? diff --git a/Core/MIPS/x86/X64IRRegCache.cpp b/Core/MIPS/x86/X64IRRegCache.cpp index 5b39b28de1..5bbe332e89 100644 --- a/Core/MIPS/x86/X64IRRegCache.cpp +++ b/Core/MIPS/x86/X64IRRegCache.cpp @@ -65,7 +65,15 @@ const int *X64IRRegCache::GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &c #endif }; -#if !PPSSPP_ARCH(X86) +#if PPSSPP_ARCH(X86) + if ((flags & X64Map::LOW_SUBREG) == X64Map::LOW_SUBREG) { + static const int lowSubRegAllocationOrder[] = { + EDX, EBX, ECX, + }; + count = ARRAY_SIZE(lowSubRegAllocationOrder); + return lowSubRegAllocationOrder; + } +#else if (jo_->reserveR15ForAsm) { count = ARRAY_SIZE(allocationOrder) - 1; return allocationOrder; @@ -122,11 +130,17 @@ void X64IRRegCache::FlushBeforeCall() { #endif } -X64Reg X64IRRegCache::TryMapTempImm(IRReg r) { +X64Reg X64IRRegCache::TryMapTempImm(IRReg r, X64Map flags) { _dbg_assert_(IsValidGPR(r)); + + auto canUseReg = [flags](X64Reg r) { + return (flags & X64Map::LOW_SUBREG) != X64Map::LOW_SUBREG || HasLowSubregister(r); + }; + // If already mapped, no need for a temporary. if (IsGPRMapped(r)) { - return RX(r); + if (canUseReg(RX(r))) + return RX(r); } if (mr[r].loc == MIPSLoc::IMM) { @@ -134,7 +148,8 @@ X64Reg X64IRRegCache::TryMapTempImm(IRReg r) { for (int i = 0; i < TOTAL_MAPPABLE_IRREGS; ++i) { if (mr[i].loc == MIPSLoc::REG_IMM && mr[i].imm == mr[r].imm) { // Awesome, let's just use this reg. - return FromNativeReg(mr[i].nReg); + if (canUseReg(FromNativeReg(mr[i].nReg))) + return FromNativeReg(mr[i].nReg); } } } @@ -157,6 +172,11 @@ X64Reg X64IRRegCache::MapWithFPRTemp(IRInst &inst) { X64Reg X64IRRegCache::MapGPR(IRReg mipsReg, MIPSMap mapFlags) { _dbg_assert_(IsValidGPR(mipsReg)); + if ((mapFlags & X64Map::LOW_SUBREG) == X64Map::LOW_SUBREG && IsGPRMapped(mipsReg) && !HasLowSubregister(RX(mipsReg))) { + // Unfortunate. For now, let's flush it. We'll realloc a better one. + FlushNativeReg(mr[mipsReg].nReg); + } + // Okay, not mapped, so we need to allocate an RV register. IRNativeReg nreg = MapNativeReg(MIPSLoc::REG, mipsReg, 1, mapFlags); return FromNativeReg(nreg); @@ -336,4 +356,13 @@ X64Reg X64IRRegCache::FX(IRReg mipsReg) { } } +bool X64IRRegCache::HasLowSubregister(Gen::X64Reg reg) { +#if !PPSSPP_ARCH(AMD64) + // Can't use ESI or EDI (which we use), no 8-bit versions. Only these. + return reg == EAX || reg == EBX || reg == ECX || reg == EDX; +#else + return true; +#endif +} + #endif diff --git a/Core/MIPS/x86/X64IRRegCache.h b/Core/MIPS/x86/X64IRRegCache.h index 3e5909591b..259c03f849 100644 --- a/Core/MIPS/x86/X64IRRegCache.h +++ b/Core/MIPS/x86/X64IRRegCache.h @@ -41,6 +41,23 @@ static constexpr auto tempOffset = offsetof(MIPSState, temp) - 128; static constexpr auto fcr31Offset = offsetof(MIPSState, fcr31) - 128; static constexpr auto pcOffset = offsetof(MIPSState, pc) - 128; +enum class X64Map : uint8_t { + NONE = 0, + LOW_SUBREG = 0x10, +}; +static inline MIPSMap operator |(const MIPSMap &lhs, const X64Map &rhs) { + return MIPSMap((uint8_t)lhs | (uint8_t)rhs); +} +static inline X64Map operator |(const X64Map &lhs, const X64Map &rhs) { + return X64Map((uint8_t)lhs | (uint8_t)rhs); +} +static inline X64Map operator &(const MIPSMap &lhs, const X64Map &rhs) { + return X64Map((uint8_t)lhs & (uint8_t)rhs); +} +static inline X64Map operator &(const X64Map &lhs, const X64Map &rhs) { + return X64Map((uint8_t)lhs & (uint8_t)rhs); +} + } // namespace X64IRJitConstants class X64IRRegCache : public IRNativeRegCacheBase { @@ -50,7 +67,7 @@ public: void Init(Gen::XEmitter *emitter); // May fail and return INVALID_REG if it needs flushing. - Gen::X64Reg TryMapTempImm(IRReg); + Gen::X64Reg TryMapTempImm(IRReg, X64IRJitConstants::X64Map flags = X64IRJitConstants::X64Map::NONE); // Returns an RV register containing the requested MIPS register. Gen::X64Reg MapGPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); @@ -70,6 +87,8 @@ public: Gen::X64Reg RXPtr(IRReg preg); // Returns a cached register, if it has been mapped as a pointer Gen::X64Reg FX(IRReg preg); + static bool HasLowSubregister(Gen::X64Reg reg); + protected: const int *GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &count, int &base) const override; void AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) override; From 07fa1ed573b5d3598126896e298663c7aa9004d9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 21 Aug 2023 21:16:54 -0700 Subject: [PATCH 33/36] x86jit: Automatically flush incompatible regs. --- Core/MIPS/IR/IRRegCache.cpp | 31 +++++++++++++++++++++++++------ Core/MIPS/IR/IRRegCache.h | 1 + Core/MIPS/RiscV/RiscVRegCache.cpp | 7 +++++++ Core/MIPS/RiscV/RiscVRegCache.h | 1 + Core/MIPS/x86/X64IRRegCache.cpp | 5 ----- 5 files changed, 34 insertions(+), 11 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index 0141041500..931f2941c6 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -434,6 +434,19 @@ IRNativeReg IRNativeRegCacheBase::FindBestToSpill(MIPSLoc type, MIPSMap flags, b return -1; } +bool IRNativeRegCacheBase::IsNativeRegCompatible(IRNativeReg nreg, MIPSLoc type, MIPSMap flags) { + int allocCount = 0, base = 0; + const int *allocOrder = GetAllocationOrder(type, flags, allocCount, base); + + for (int i = 0; i < allocCount; i++) { + IRNativeReg allocReg = IRNativeReg(allocOrder[i] - base); + if (allocReg == nreg) + return true; + } + + return false; +} + void IRNativeRegCacheBase::DiscardNativeReg(IRNativeReg nreg) { _assert_msg_(nreg >= 0 && nreg < config_.totalNativeRegs, "DiscardNativeReg on invalid register %d", nreg); if (nr[nreg].mipsReg != IRREG_INVALID) { @@ -828,18 +841,24 @@ IRNativeReg IRNativeRegCacheBase::MapNativeReg(MIPSLoc type, IRReg first, int la case MIPSLoc::REG_IMM: case MIPSLoc::REG_AS_PTR: case MIPSLoc::REG: - if (type != MIPSLoc::REG) + if (type != MIPSLoc::REG) { nreg = AllocateReg(type, flags); + } else if (!IsNativeRegCompatible(nreg, type, flags)) { + // If it's not compatible, we'll need to reallocate. + // TODO: Could do a transfer and avoid memory flush. + FlushNativeReg(nreg); + nreg = AllocateReg(type, flags); + } break; case MIPSLoc::FREG: - if (type != MIPSLoc::FREG) - nreg = AllocateReg(type, flags); - break; - case MIPSLoc::VREG: - if (type != MIPSLoc::VREG) + if (type != mr[first].loc) { nreg = AllocateReg(type, flags); + } else if (!IsNativeRegCompatible(nreg, type, flags)) { + FlushNativeReg(nreg); + nreg = AllocateReg(type, flags); + } break; case MIPSLoc::IMM: diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index 2d162a835b..63a2eb4df3 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -201,6 +201,7 @@ protected: IRNativeReg AllocateReg(MIPSLoc type, MIPSMap flags); IRNativeReg FindFreeReg(MIPSLoc type, MIPSMap flags) const; IRNativeReg FindBestToSpill(MIPSLoc type, MIPSMap flags, bool unusedOnly, bool *clobbered) const; + virtual bool IsNativeRegCompatible(IRNativeReg nreg, MIPSLoc type, MIPSMap flags); virtual void DiscardNativeReg(IRNativeReg nreg); virtual void FlushNativeReg(IRNativeReg nreg); virtual void DiscardReg(IRReg mreg); diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index a39dd6b344..d8bf178363 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -300,6 +300,13 @@ void RiscVRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { } } +bool RiscVRegCache::IsNativeRegCompatible(IRNativeReg nreg, MIPSLoc type, MIPSMap flags) { + // No special flags except VREG, skip the check for a little speed. + if (type != MIPSLoc::VREG) + return true; + return IRNativeRegCacheBase::IsNativeRegCompatible(nreg, type, flags); +} + void RiscVRegCache::LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) { RiscVReg r = (RiscVReg)(X0 + nreg); _dbg_assert_(r > X0); diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index c1b140896f..a11aedb09f 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -76,6 +76,7 @@ protected: const int *GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &count, int &base) const override; void AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) override; + bool IsNativeRegCompatible(IRNativeReg nreg, MIPSLoc type, MIPSMap flags) override; void LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; void StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) override; void SetNativeRegValue(IRNativeReg nreg, uint32_t imm) override; diff --git a/Core/MIPS/x86/X64IRRegCache.cpp b/Core/MIPS/x86/X64IRRegCache.cpp index 5bbe332e89..f1a241ce9f 100644 --- a/Core/MIPS/x86/X64IRRegCache.cpp +++ b/Core/MIPS/x86/X64IRRegCache.cpp @@ -172,11 +172,6 @@ X64Reg X64IRRegCache::MapWithFPRTemp(IRInst &inst) { X64Reg X64IRRegCache::MapGPR(IRReg mipsReg, MIPSMap mapFlags) { _dbg_assert_(IsValidGPR(mipsReg)); - if ((mapFlags & X64Map::LOW_SUBREG) == X64Map::LOW_SUBREG && IsGPRMapped(mipsReg) && !HasLowSubregister(RX(mipsReg))) { - // Unfortunate. For now, let's flush it. We'll realloc a better one. - FlushNativeReg(mr[mipsReg].nReg); - } - // Okay, not mapped, so we need to allocate an RV register. IRNativeReg nreg = MapNativeReg(MIPSLoc::REG, mipsReg, 1, mapFlags); return FromNativeReg(nreg); From edcb1568972e5d65976f95ae8dfd51901f2ce9da Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 21 Aug 2023 23:23:59 -0700 Subject: [PATCH 34/36] x86jit: Add Vec4 and Float load/store. --- Core/MIPS/IR/IRRegCache.cpp | 2 +- Core/MIPS/x86/X64IRCompLoadStore.cpp | 20 ++++++++++++++++---- Core/MIPS/x86/X64IRRegCache.cpp | 14 ++++++++++++-- Core/MIPS/x86/X64IRRegCache.h | 1 + 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index 931f2941c6..dd4f49cdec 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -907,7 +907,7 @@ void IRNativeRegCacheBase::MapNativeReg(MIPSLoc type, IRNativeReg nreg, IRReg fi } // If it's still in a different reg, either discard or possibly transfer. - if (mreg.nReg != -1 && mreg.nReg != nreg) { + if (mreg.nReg != -1 && (mreg.nReg != nreg || mismatch)) { _assert_msg_(!mreg.isStatic, "Cannot MapNativeReg a static reg to a new reg"); if ((flags & MIPSMap::NOINIT) != MIPSMap::NOINIT) { // We better not be trying to map to a different nreg if it's in one now. diff --git a/Core/MIPS/x86/X64IRCompLoadStore.cpp b/Core/MIPS/x86/X64IRCompLoadStore.cpp index 09f0269c7d..534e3a2319 100644 --- a/Core/MIPS/x86/X64IRCompLoadStore.cpp +++ b/Core/MIPS/x86/X64IRCompLoadStore.cpp @@ -90,9 +90,12 @@ void X64JitBackend::CompIR_CondStore(IRInst inst) { void X64JitBackend::CompIR_FLoad(IRInst inst) { CONDITIONAL_DISABLE; + OpArg addrArg = PrepareSrc1Address(inst); + switch (inst.op) { case IROp::LoadFloat: - CompIR_Generic(inst); + regs_.MapFPR(inst.dest, MIPSMap::NOINIT); + MOVSS(regs_.FX(inst.dest), addrArg); break; default: @@ -104,9 +107,12 @@ void X64JitBackend::CompIR_FLoad(IRInst inst) { void X64JitBackend::CompIR_FStore(IRInst inst) { CONDITIONAL_DISABLE; + OpArg addrArg = PrepareSrc1Address(inst); + switch (inst.op) { case IROp::StoreFloat: - CompIR_Generic(inst); + regs_.MapFPR(inst.src3); + MOVSS(addrArg, regs_.FX(inst.dest)); break; default: @@ -241,9 +247,12 @@ void X64JitBackend::CompIR_StoreShift(IRInst inst) { void X64JitBackend::CompIR_VecLoad(IRInst inst) { CONDITIONAL_DISABLE; + OpArg addrArg = PrepareSrc1Address(inst); + switch (inst.op) { case IROp::LoadVec4: - CompIR_Generic(inst); + regs_.MapVec4(inst.dest, MIPSMap::NOINIT); + MOVUPS(regs_.FX(inst.dest), addrArg); break; default: @@ -255,9 +264,12 @@ void X64JitBackend::CompIR_VecLoad(IRInst inst) { void X64JitBackend::CompIR_VecStore(IRInst inst) { CONDITIONAL_DISABLE; + OpArg addrArg = PrepareSrc1Address(inst); + switch (inst.op) { case IROp::StoreVec4: - CompIR_Generic(inst); + regs_.MapVec4(inst.src3); + MOVUPS(addrArg, regs_.FX(inst.dest)); break; default: diff --git a/Core/MIPS/x86/X64IRRegCache.cpp b/Core/MIPS/x86/X64IRRegCache.cpp index f1a241ce9f..07c1be33a3 100644 --- a/Core/MIPS/x86/X64IRRegCache.cpp +++ b/Core/MIPS/x86/X64IRRegCache.cpp @@ -35,9 +35,8 @@ using namespace X64IRJitConstants; X64IRRegCache::X64IRRegCache(MIPSComp::JitOptions *jo) : IRNativeRegCacheBase(jo) { - // TODO: Enable SIMD. config_.totalNativeRegs = NUM_X_REGS + NUM_X_FREGS; - config_.mapFPUSIMD = false; + config_.mapFPUSIMD = true; // XMM regs are used for both FPU and Vec, so we don't need VREGs. config_.mapUseVRegs = false; } @@ -191,6 +190,17 @@ X64Reg X64IRRegCache::MapFPR(IRReg mipsReg, MIPSMap mapFlags) { return INVALID_REG; } +X64Reg X64IRRegCache::MapVec4(IRReg first, MIPSMap mapFlags) { + _dbg_assert_(IsValidFPR(mipsReg)); + _dbg_assert_((mipsReg & 3) == 0); + _dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::MEM || mr[mipsReg + 32].loc == MIPSLoc::FREG); + + IRNativeReg nreg = MapNativeReg(MIPSLoc::FREG, first + 32, 4, mapFlags); + if (nreg != -1) + return FromNativeReg(nreg); + return INVALID_REG; +} + void X64IRRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { _assert_(nreg >= 0 && nreg < NUM_X_REGS); X64Reg r = FromNativeReg(nreg); diff --git a/Core/MIPS/x86/X64IRRegCache.h b/Core/MIPS/x86/X64IRRegCache.h index 259c03f849..e4d3bdb592 100644 --- a/Core/MIPS/x86/X64IRRegCache.h +++ b/Core/MIPS/x86/X64IRRegCache.h @@ -73,6 +73,7 @@ public: Gen::X64Reg MapGPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); Gen::X64Reg MapGPRAsPointer(IRReg reg); Gen::X64Reg MapFPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT); + Gen::X64Reg MapVec4(IRReg first, MIPSMap mapFlags = MIPSMap::INIT); Gen::X64Reg MapWithFPRTemp(IRInst &inst); From 521b63dd2b3fc7edf42f0c2ce646864dfe7a341c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 21 Aug 2023 23:51:46 -0700 Subject: [PATCH 35/36] x86jit: Implement FMul. --- Core/MIPS/x86/X64IRCompFPU.cpp | 41 ++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Core/MIPS/x86/X64IRCompFPU.cpp b/Core/MIPS/x86/X64IRCompFPU.cpp index e143865ca1..5dfa065088 100644 --- a/Core/MIPS/x86/X64IRCompFPU.cpp +++ b/Core/MIPS/x86/X64IRCompFPU.cpp @@ -36,13 +36,54 @@ namespace MIPSComp { using namespace Gen; using namespace X64IRJitConstants; +alignas(16) const u32 reverseQNAN[4] = { 0x803FFFFF, 0x803FFFFF, 0x803FFFFF, 0x803FFFFF }; + void X64JitBackend::CompIR_FArith(IRInst inst) { CONDITIONAL_DISABLE; switch (inst.op) { case IROp::FAdd: case IROp::FSub: + CompIR_Generic(inst); + break; + case IROp::FMul: + { + X64Reg tempReg = regs_.MapWithFPRTemp(inst); + + // tempReg = !my_isnan(src1) && !my_isnan(src2) + MOVSS(tempReg, regs_.F(inst.src1)); + CMPORDSS(tempReg, regs_.F(inst.src2)); + if (inst.dest == inst.src1) { + MULSS(regs_.FX(inst.dest), regs_.F(inst.src2)); + } else if (inst.dest == inst.src2) { + MULSS(regs_.FX(inst.dest), regs_.F(inst.src1)); + } else if (cpu_info.bAVX) { + VMULSS(regs_.FX(inst.dest), regs_.FX(inst.src1), regs_.F(inst.src2)); + } else { + MOVAPS(regs_.FX(inst.dest), regs_.F(inst.src1)); + MULSS(regs_.FX(inst.dest), regs_.F(inst.src2)); + } + + // Abuse a lane of tempReg to remember dest: NAN, NAN, res, res. + SHUFPS(tempReg, regs_.F(inst.dest), 0); + // dest = my_isnan(dest) && !my_isnan(src1) && !my_isnan(src2) + CMPUNORDSS(regs_.FX(inst.dest), regs_.F(inst.dest)); + ANDPS(regs_.FX(inst.dest), R(tempReg)); + // At this point fd = FFFFFFFF if non-NAN inputs produced a NAN output. + // We'll AND it with the inverse QNAN bits to clear (00000000 means no change.) + if (RipAccessible(&reverseQNAN)) { + ANDPS(regs_.FX(inst.dest), M(&reverseQNAN)); // rip accessible + } else { + MOV(PTRBITS, R(SCRATCH1), ImmPtr(&reverseQNAN)); + ANDPS(regs_.FX(inst.dest), MatR(SCRATCH1)); + } + // ANDN is backwards, which is why we saved XMM0 to start. Now put it back. + SHUFPS(tempReg, R(tempReg), 0xFF); + ANDNPS(regs_.FX(inst.dest), R(tempReg)); + break; + } + case IROp::FDiv: case IROp::FSqrt: case IROp::FNeg: From 11c40e6889c0936318bde223b1e840e99aa1c119 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 22 Aug 2023 00:00:51 -0700 Subject: [PATCH 36/36] x86: Implement a few basic float/vec4 ops. --- Core/MIPS/x86/X64IRCompFPU.cpp | 36 +++++++++++++++++++++++++++++++++- Core/MIPS/x86/X64IRCompVec.cpp | 6 +++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/Core/MIPS/x86/X64IRCompFPU.cpp b/Core/MIPS/x86/X64IRCompFPU.cpp index 5dfa065088..e9c1b9f683 100644 --- a/Core/MIPS/x86/X64IRCompFPU.cpp +++ b/Core/MIPS/x86/X64IRCompFPU.cpp @@ -43,8 +43,36 @@ void X64JitBackend::CompIR_FArith(IRInst inst) { switch (inst.op) { case IROp::FAdd: + regs_.Map(inst); + if (inst.dest == inst.src1) { + ADDSS(regs_.FX(inst.dest), regs_.F(inst.src2)); + } else if (inst.dest == inst.src2) { + ADDSS(regs_.FX(inst.dest), regs_.F(inst.src1)); + } else if (cpu_info.bAVX) { + VADDSS(regs_.FX(inst.dest), regs_.FX(inst.src1), regs_.F(inst.src2)); + } else { + MOVAPS(regs_.FX(inst.dest), regs_.F(inst.src1)); + ADDSS(regs_.FX(inst.dest), regs_.F(inst.src2)); + } + break; + case IROp::FSub: - CompIR_Generic(inst); + if (inst.dest == inst.src1) { + regs_.Map(inst); + SUBSS(regs_.FX(inst.dest), regs_.F(inst.src2)); + } else if (cpu_info.bAVX) { + regs_.Map(inst); + VSUBSS(regs_.FX(inst.dest), regs_.FX(inst.src1), regs_.F(inst.src2)); + } else if (inst.dest == inst.src2) { + X64Reg tempReg = regs_.MapWithFPRTemp(inst); + MOVAPS(tempReg, regs_.F(inst.src2)); + MOVAPS(regs_.FX(inst.dest), regs_.F(inst.src1)); + SUBSS(regs_.FX(inst.dest), R(tempReg)); + } else { + regs_.Map(inst); + MOVAPS(regs_.FX(inst.dest), regs_.F(inst.src1)); + SUBSS(regs_.FX(inst.dest), regs_.F(inst.src2)); + } break; case IROp::FMul: @@ -101,6 +129,12 @@ void X64JitBackend::CompIR_FAssign(IRInst inst) { switch (inst.op) { case IROp::FMov: + if (inst.dest != inst.src1) { + regs_.Map(inst); + MOVAPS(regs_.FX(inst.dest), regs_.F(inst.src1)); + } + break; + case IROp::FAbs: case IROp::FSign: CompIR_Generic(inst); diff --git a/Core/MIPS/x86/X64IRCompVec.cpp b/Core/MIPS/x86/X64IRCompVec.cpp index 051c8c0fd7..cc7af5025f 100644 --- a/Core/MIPS/x86/X64IRCompVec.cpp +++ b/Core/MIPS/x86/X64IRCompVec.cpp @@ -65,10 +65,14 @@ void X64JitBackend::CompIR_VecAssign(IRInst inst) { case IROp::Vec4Init: case IROp::Vec4Shuffle: case IROp::Vec4Blend: - case IROp::Vec4Mov: CompIR_Generic(inst); break; + case IROp::Vec4Mov: + regs_.Map(inst); + MOVAPS(regs_.FX(inst.dest), regs_.F(inst.src1)); + break; + default: INVALIDOP; break;