diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1778c9b734..4d6033eada 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1611,6 +1611,17 @@ 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/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
+ Core/MIPS/x86/X64IRRegCache.h
GPU/Common/VertexDecoderX86.cpp
GPU/Software/DrawPixelX86.cpp
GPU/Software/SamplerX86.cpp
@@ -1633,8 +1644,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/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() {
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 b179805790..bd3fb17ad7 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -605,7 +605,14 @@
-
+
+
+
+
+
+
+
+
@@ -1092,6 +1099,7 @@
+
@@ -1179,7 +1187,7 @@
-
+
@@ -1431,6 +1439,7 @@
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index 627456ae70..15964c072c 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -1216,9 +1216,6 @@
MIPS\RiscV
-
- MIPS\RiscV
-
MIPS\RiscV
@@ -1246,6 +1243,33 @@
Core
+
+ MIPS\x86
+
+
+ MIPS\x86
+
+
+ MIPS\x86
+
+
+ MIPS\x86
+
+
+ MIPS\x86
+
+
+ MIPS\x86
+
+
+ MIPS\x86
+
+
+ MIPS\x86
+
+
+ MIPS\x86
+
@@ -1998,9 +2022,6 @@
MIPS\RiscV
-
- MIPS\RiscV
-
MIPS\IR
@@ -2010,6 +2031,12 @@
Core
+
+ MIPS\x86
+
+
+ MIPS\x86
+
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;
diff --git a/Core/MIPS/IR/IRAnalysis.cpp b/Core/MIPS/IR/IRAnalysis.cpp
index 2de3adb8d1..e27554d712 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)
+ return true;
+ if ((m->flags & IRFLAG_EXIT) != 0)
+ 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/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;
}
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/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..323b7ae2f9 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,46 +954,65 @@ 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];
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.
@@ -1018,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;
@@ -1043,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) {
diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp
index f3b45bef71..dd4f49cdec 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]);
}
}
@@ -102,7 +111,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 +132,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;
@@ -282,6 +291,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))
@@ -316,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) {
@@ -337,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....
@@ -346,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);
@@ -361,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;
@@ -420,15 +434,29 @@ IRNativeReg IRNativeRegCacheBase::FindBestToSpill(MIPSLoc type, bool unusedOnly,
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 < 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;
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);
@@ -460,7 +488,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);
@@ -563,12 +591,20 @@ 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.
+ 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))
+ 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 +635,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)
@@ -614,13 +654,178 @@ 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) {
+ Mapping mapping[3];
+ MappingFromInst(inst, mapping);
+
+ ApplyMapping(mapping, 3);
+ 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);
+
+ ApplyMapping(mapping, 3);
+ // Grab a temp while things are spill locked.
+ IRNativeReg temp = AllocateReg(type, MIPSMap::INIT);
+ 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_.mapFPUSIMD && mapping[i].type != 'G') {
+ 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_.mapFPUSIMD || mapping[i].type == 'G') {
+ 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_.mapFPUSIMD && mapping[i].type != 'G') {
+ 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_.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);
+ } 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);
+ for (int i = 0; i < 3; ++i) {
+ switch (m->types[i]) {
+ case 'G':
+ mapping[i].type = 'G';
+ _assert_msg_(IsValidGPR(mapping[i].reg), "G was not valid GPR?");
+ break;
+
+ case 'F':
+ 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':
+ 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':
+ 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':
+ case '_':
+ case 'C':
+ case 'I':
+ case 'v':
+ case 's':
+ case 'm':
+ mapping[i].type = '_';
+ mapping[i].reg = IRREG_INVALID;
+ mapping[i].lanes = 0;
+ break;
+
+ default:
+ _assert_msg_(mapping[i].reg == IRREG_INVALID, "Unexpected register type %c", m->types[i]);
+ break;
+ }
+ }
+
+ if (mapping[0].type != '_') {
+ if ((m->flags & IRFLAG_SRC3DST) != 0)
+ mapping[0].flags = MIPSMap::DIRTY;
+ else if ((m->flags & IRFLAG_SRC3) != 0)
+ mapping[0].flags = MIPSMap::INIT;
+ else
+ mapping[0].flags = MIPSMap::NOINIT;
+ }
+}
+
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);
@@ -636,23 +841,29 @@ 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)
- nreg = AllocateReg(type);
+ 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);
- break;
-
case MIPSLoc::VREG:
- if (type != MIPSLoc::VREG)
- nreg = AllocateReg(type);
+ 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:
case MIPSLoc::MEM:
- nreg = AllocateReg(type);
+ nreg = AllocateReg(type, flags);
break;
}
}
@@ -696,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.
@@ -730,7 +941,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.
@@ -806,10 +1017,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 33779b701a..63a2eb4df3 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,27 +68,23 @@ 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_;
};
// Initing is the default so the flag is reversed.
-enum class MIPSMap {
+// 8 bits - upper 4 are reserved for backend purposes.
+enum class MIPSMap : uint8_t {
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);
+ 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 {
@@ -183,19 +179,29 @@ public:
void MarkGPRDirty(IRReg gpr, bool andNormalized32 = false);
void MarkGPRAsPointerDirty(IRReg gpr);
- virtual void FlushAll();
+ struct Mapping {
+ char type = '?';
+ IRReg reg = IRREG_INVALID;
+ uint8_t lanes = 1;
+ MIPSMap flags = MIPSMap::INIT;
+ };
+
+ void Map(const IRInst &inst);
+ void MapWithExtra(const IRInst &inst, std::vector extra);
+ virtual void FlushAll(bool gprs = true, bool fprs = true);
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 bool IsNativeRegCompatible(IRNativeReg nreg, MIPSLoc type, MIPSMap flags);
virtual void DiscardNativeReg(IRNativeReg nreg);
virtual void FlushNativeReg(IRNativeReg nreg);
virtual void DiscardReg(IRReg mreg);
@@ -205,6 +211,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 +227,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;
@@ -224,7 +237,12 @@ protected:
MIPSComp::JitOptions *jo_;
const MIPSComp::IRBlock *irBlock_ = nullptr;
int irIndex_ = 0;
- int totalNativeRegs_ = 0;
+
+ struct {
+ int totalNativeRegs = 0;
+ bool mapFPUSIMD = false;
+ bool mapUseVRegs = false;
+ } config_;
RegStatusNative nr[TOTAL_POSSIBLE_NATIVEREGS];
RegStatusMIPS mr[TOTAL_MAPPABLE_IRREGS];
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/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 69e4a47ee5..52a13f3fd9 100644
--- a/Core/MIPS/RiscV/RiscVCompALU.cpp
+++ b/Core/MIPS/RiscV/RiscVCompALU.cpp
@@ -53,41 +53,47 @@ 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);
- ADDW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
+ 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.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32);
- SUBW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
+ 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.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32);
- ADDIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant);
+ regs_.Map(inst);
+ ADDIW(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant);
+ regs_.MarkGPRDirty(inst.dest, true);
}
} else {
- gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32);
+ regs_.Map(inst);
LI(SCRATCH1, (int32_t)inst.constant);
- ADDW(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1);
+ ADDW(regs_.R(inst.dest), regs_.R(inst.src1), SCRATCH1);
+ regs_.MarkGPRDirty(inst.dest, true);
}
break;
case IROp::SubConst:
- gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32);
+ regs_.Map(inst);
LI(SCRATCH1, (int32_t)inst.constant);
- SUBW(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1);
+ SUBW(regs_.R(inst.dest), regs_.R(inst.src1), SCRATCH1);
+ regs_.MarkGPRDirty(inst.dest, true);
break;
case IROp::Neg:
- gpr.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32);
- SUBW(gpr.R(inst.dest), R_ZERO, gpr.R(inst.src1));
+ regs_.Map(inst);
+ SUBW(regs_.R(inst.dest), R_ZERO, regs_.R(inst.src1));
+ regs_.MarkGPRDirty(inst.dest, true);
break;
default:
@@ -99,86 +105,85 @@ 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);
- 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.MapDirtyIn(inst.dest, inst.src1);
- 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) {
- 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 = 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.MapDirtyIn(inst.dest, inst.src1);
- 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.MapDirtyInIn(inst.dest, inst.src1, inst.src2);
- 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 = regs_.IsNormalized32(inst.src1);
+ regs_.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);
+ ANDI(regs_.R(inst.dest), regs_.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);
+ 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 && gpr.IsNormalized32(inst.src1))
- gpr.MarkGPRDirty(inst.dest, true);
+ if ((inst.constant & 0x80000000) != 0 && resultNormalized)
+ 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 = regs_.IsNormalized32(inst.src1);
+ regs_.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);
+ ORI(regs_.R(inst.dest), regs_.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);
+ OR(regs_.R(inst.dest), regs_.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);
+ regs_.MarkGPRDirty(inst.dest, resultNormalized);
break;
case IROp::XorConst:
+ regs_.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);
+ XORI(regs_.R(inst.dest), regs_.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);
+ XOR(regs_.R(inst.dest), regs_.R(inst.src1), SCRATCH1);
}
break;
case IROp::Not:
- gpr.MapDirtyIn(inst.dest, inst.src1);
- NOT(gpr.R(inst.dest), gpr.R(inst.src1));
+ regs_.Map(inst);
+ NOT(regs_.R(inst.dest), regs_.R(inst.src1));
break;
default:
@@ -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);
- 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:
+ regs_.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));
+ SEXT_B(regs_.R(inst.dest), regs_.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);
+ SLLI(regs_.R(inst.dest), regs_.R(inst.src1), 24);
+ SRAIW(regs_.R(inst.dest), regs_.R(inst.dest), 24);
}
+ regs_.MarkGPRDirty(inst.dest, true);
break;
case IROp::Ext16to32:
+ regs_.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));
+ SEXT_H(regs_.R(inst.dest), regs_.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);
+ SLLI(regs_.R(inst.dest), regs_.R(inst.src1), 16);
+ SRAIW(regs_.R(inst.dest), regs_.R(inst.dest), 16);
}
+ regs_.MarkGPRDirty(inst.dest, true);
break;
default:
@@ -233,37 +238,37 @@ void RiscVJitBackend::CompIR_Bits(IRInst inst) {
switch (inst.op) {
case IROp::ReverseBits:
if (cpu_info.RiscV_Zbb) {
- gpr.MapDirtyIn(inst.dest, inst.src1);
+ 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);
}
@@ -275,12 +280,12 @@ void RiscVJitBackend::CompIR_Bits(IRInst inst) {
case IROp::BSwap32:
if (cpu_info.RiscV_Zbb) {
- gpr.MapDirtyIn(inst.dest, inst.src1);
- 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);
@@ -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);
+ regs_.Map(inst);
// This even sets to 32 when zero, perfect.
- CLZW(gpr.R(inst.dest), gpr.R(inst.src1));
+ CLZW(regs_.R(inst.dest), regs_.R(inst.src1));
+ regs_.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);
- SLLW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
+ 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.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32);
- SRLW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
+ 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.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32);
- SRAW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
+ 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.MapDirtyInIn(inst.dest, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32);
- RORW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
+ 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);
}
@@ -334,62 +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.MapDirtyIn(inst.dest, inst.src1);
- 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.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32);
- SLLIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2);
+ 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.MapDirtyIn(inst.dest, inst.src1);
- 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.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32);
- SRLIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2);
+ 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.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32);
- SRAIW(gpr.R(inst.dest), gpr.R(inst.src1), 31);
+ 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.MapDirtyIn(inst.dest, inst.src1);
- 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.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32);
- SRAIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2);
+ 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.MapDirtyIn(inst.dest, inst.src1);
- 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.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32);
- RORIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2 & 31);
+ regs_.Map(inst);
+ RORIW(regs_.R(inst.dest), regs_.R(inst.src1), inst.src2 & 31);
+ regs_.MarkGPRDirty(inst.dest, true);
} else {
CompIR_Generic(inst);
}
@@ -408,64 +423,58 @@ 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);
+ regs_.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);
+ 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.MapDirtyIn(inst.dest, inst.src1, MapType::AVOID_LOAD_MARK_NORM32);
- SRLIW(gpr.R(inst.dest), gpr.R(inst.src1), 31);
+ regs_.Map(inst);
+ SRLIW(regs_.R(inst.dest), regs_.R(inst.src1), 31);
+ regs_.MarkGPRDirty(inst.dest, true);
} else {
- gpr.SpillLockGPR(inst.dest, inst.src1);
- gpr.MapReg(inst.src1);
+ regs_.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);
+ 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.SpillLockGPR(inst.dest, inst.src1, inst.src2);
- gpr.MapReg(inst.src1);
- gpr.MapReg(inst.src2);
+ regs_.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);
+ 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.SpillLockGPR(inst.dest, inst.src1);
- gpr.MapReg(inst.src1);
+ regs_.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.
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);
}
+ regs_.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.
+ regs_.Map(inst);
NormalizeSrc1(inst, &lhs, SCRATCH1, true);
switch (inst.op) {
@@ -503,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.MapDirtyInIn(inst.dest, inst.src1, inst.src2);
+ 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.MapDirtyIn(inst.dest, inst.src1);
- 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.MapDirtyInIn(inst.dest, inst.src1, inst.src2);
+ 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.MapDirtyIn(inst.dest, inst.src1);
- 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;
@@ -554,27 +563,43 @@ 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_.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:
- gpr.MapDirtyIn(IRREG_HI, inst.src1);
- MV(gpr.R(IRREG_HI), gpr.R(inst.src1));
- gpr.MarkGPRDirty(IRREG_HI, gpr.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:
- gpr.MapDirtyIn(inst.dest, IRREG_LO);
- MV(gpr.R(inst.dest), gpr.R(IRREG_LO));
- gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(IRREG_LO));
+ 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));
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_.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:
@@ -588,92 +613,62 @@ 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;
*rhs = SCRATCH2;
};
- 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));
- } else {
- SLLI(gpr.R(IRREG_LO), gpr.R(IRREG_LO), XLEN - 32);
- SRLI(gpr.R(IRREG_LO), gpr.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));
- };
- auto splitMulResult = [&] {
- SRAI(gpr.R(IRREG_HI), gpr.R(IRREG_LO), 32);
- gpr.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...
- gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2);
+ regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::NOINIT } });
NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true);
- MUL(gpr.R(IRREG_LO), lhs, rhs);
- splitMulResult();
+ MUL(regs_.R(IRREG_LO), lhs, rhs);
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);
+ // 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(gpr.R(IRREG_LO), lhs, rhs);
- splitMulResult();
+ MUL(regs_.R(IRREG_LO), lhs, rhs);
break;
case IROp::Madd:
- gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD);
+ regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });
NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true);
MUL(SCRATCH1, lhs, rhs);
-
- combinePrevMulResult();
- ADD(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1);
- splitMulResult();
+ ADD(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1);
break;
case IROp::MaddU:
- gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD);
+ regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });
makeArgsUnsigned(&lhs, &rhs);
MUL(SCRATCH1, lhs, rhs);
-
- combinePrevMulResult();
- ADD(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1);
- splitMulResult();
+ ADD(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1);
break;
case IROp::Msub:
- gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD);
+ regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });
NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true);
MUL(SCRATCH1, lhs, rhs);
-
- combinePrevMulResult();
- SUB(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1);
- splitMulResult();
+ SUB(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1);
break;
case IROp::MsubU:
- gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD);
+ regs_.MapWithExtra(inst, { { 'G', IRREG_LO, 2, MIPSMap::DIRTY } });
makeArgsUnsigned(&lhs, &rhs);
MUL(SCRATCH1, lhs, rhs);
-
- combinePrevMulResult();
- SUB(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1);
- splitMulResult();
+ SUB(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1);
break;
default:
@@ -688,18 +683,30 @@ 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_.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(gpr.R(IRREG_LO), numReg, denomReg);
- REMW(gpr.R(IRREG_HI), numReg, denomReg);
+ DIVW(regs_.R(IRREG_LO), 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(gpr.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);
@@ -709,18 +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(gpr.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);
}
break;
case IROp::DivU:
- gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32);
+ 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(gpr.R(IRREG_LO), numReg, denomReg);
- REMUW(gpr.R(IRREG_HI), numReg, denomReg);
+ DIVUW(regs_.R(IRREG_LO), numReg, denomReg);
+ REMUW(R_RA, numReg, denomReg);
// On divide by zero, everything is correct already except the 0xFFFF case.
{
@@ -728,10 +737,20 @@ 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);
}
+
+ // 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:
diff --git a/Core/MIPS/RiscV/RiscVCompBranch.cpp b/Core/MIPS/RiscV/RiscVCompBranch.cpp
index 4b0f141649..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"
@@ -45,7 +44,7 @@ void RiscVJitBackend::CompIR_Exit(IRInst inst) {
break;
case IROp::ExitToReg:
- exitReg = gpr.MapReg(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 +71,7 @@ void RiscVJitBackend::CompIR_ExitIf(IRInst inst) {
switch (inst.op) {
case IROp::ExitToConstIfEq:
case IROp::ExitToConstIfNeq:
- gpr.MapInIn(inst.src1, inst.src2);
+ 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 +98,7 @@ void RiscVJitBackend::CompIR_ExitIf(IRInst inst) {
case IROp::ExitToConstIfGeZ:
case IROp::ExitToConstIfLtZ:
case IROp::ExitToConstIfLeZ:
- gpr.MapReg(inst.src1);
+ regs_.Map(inst);
NormalizeSrc1(inst, &lhs, SCRATCH2, true);
FlushAll();
diff --git a/Core/MIPS/RiscV/RiscVCompFPU.cpp b/Core/MIPS/RiscV/RiscVCompFPU.cpp
index 7b92468713..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"
@@ -39,35 +38,35 @@ void RiscVJitBackend::CompIR_FArith(IRInst inst) {
switch (inst.op) {
case IROp::FAdd:
- fpr.MapDirtyInIn(inst.dest, inst.src1, inst.src2);
- 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.MapDirtyInIn(inst.dest, inst.src1, inst.src2);
- 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.MapDirtyInIn(inst.dest, inst.src1, inst.src2);
+ 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.MapDirtyInIn(inst.dest, inst.src1, inst.src2);
- 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.MapDirtyIn(inst.dest, inst.src1);
- 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.MapDirtyIn(inst.dest, inst.src1);
- 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 +82,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.MapDirtyInIn(inst.dest, inst.src1, inst.src2);
- 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 +93,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 +115,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 +128,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 +145,21 @@ void RiscVJitBackend::CompIR_FAssign(IRInst inst) {
switch (inst.op) {
case IROp::FMov:
if (inst.dest != inst.src1) {
- fpr.MapDirtyIn(inst.dest, inst.src1);
- 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.MapDirtyIn(inst.dest, inst.src1);
- 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.MapDirtyIn(inst.dest, inst.src1);
+ 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 +168,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 +189,23 @@ 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);
+ 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 +213,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 +227,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.MapDirtyIn(inst.dest, inst.src1);
- 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 +242,17 @@ void RiscVJitBackend::CompIR_FCvt(IRInst inst) {
case 3: rm = Round::DOWN; break;
}
- tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1);
+ tempReg = regs_.MapWithFPRTemp(inst);
// 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 +260,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_.MapWithFPRTemp(inst);
+ 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 +285,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_.MapWithFPRTemp(inst);
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_.MapWithFPRTemp(inst);
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;
@@ -340,140 +339,139 @@ 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:
switch (inst.dest) {
case IRFpCompareMode::False:
- gpr.SetGPRImm(IRREG_FPCOND, 0);
+ regs_.SetGPRImm(IRREG_FPCOND, 0);
break;
case IRFpCompareMode::EitherUnordered:
- fpr.MapInIn(inst.src1, inst.src2);
- gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
- FCLASS(32, SCRATCH1, fpr.R(inst.src1));
- FCLASS(32, SCRATCH2, fpr.R(inst.src2));
+ 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);
// NAN is 0x100 or 0x200.
ANDI(SCRATCH1, SCRATCH1, 0x300);
- SNEZ(gpr.R(IRREG_FPCOND), SCRATCH1);
+ SNEZ(regs_.R(IRREG_FPCOND), SCRATCH1);
+ regs_.MarkGPRDirty(IRREG_FPCOND, true);
break;
case IRFpCompareMode::EqualOrdered:
- fpr.MapInIn(inst.src1, inst.src2);
- gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
- FEQ(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2));
+ 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:
- fpr.MapInIn(inst.src1, inst.src2);
- gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
- FEQ(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2));
+ 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.
- 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);
+ regs_.MarkGPRDirty(IRREG_FPCOND, true);
break;
case IRFpCompareMode::LessEqualOrdered:
- fpr.MapInIn(inst.src1, inst.src2);
- gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
- FLE(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2));
+ 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:
- fpr.MapInIn(inst.src1, inst.src2);
- gpr.MapReg(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_.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:
- fpr.MapInIn(inst.src1, inst.src2);
- gpr.MapReg(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
- FLT(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2));
+ 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:
- fpr.MapInIn(inst.src1, inst.src2);
- gpr.MapReg(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_.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);
break;
}
break;
case IROp::FCmovVfpuCC:
- gpr.MapReg(IRREG_VFPUL_CC);
- fpr.MapDirtyIn(inst.dest, inst.src1, false);
+ regs_.MapWithExtra(inst, { { 'G', IRREG_VFPU_CC, 1, MIPSMap::INIT } });
if ((inst.src2 & 0xF) == 0) {
- ANDI(SCRATCH1, gpr.R(IRREG_VFPUL_CC), 1);
+ ANDI(SCRATCH1, regs_.R(IRREG_VFPU_CC), 1);
} else if (cpu_info.RiscV_Zbs) {
- BEXTI(SCRATCH1, gpr.R(IRREG_VFPUL_CC), inst.src2 & 0xF);
+ BEXTI(SCRATCH1, regs_.R(IRREG_VFPU_CC), inst.src2 & 0xF);
} else {
- SRLI(SCRATCH1, gpr.R(IRREG_VFPUL_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.MapReg(IRREG_VFPUL_CC, MIPSMap::DIRTY);
+ regs_.MapGPR(IRREG_VFPU_CC, MIPSMap::DIRTY);
switch (VCondition(inst.dest & 0xF)) {
case VC_EQ:
- fpr.MapInIn(inst.src1, inst.src2);
- 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.MapInIn(inst.src1, inst.src2);
+ 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.MapInIn(inst.src1, inst.src2);
- 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.MapInIn(inst.src1, inst.src2);
- 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.MapInIn(inst.src1, inst.src2);
- 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.MapInIn(inst.src1, inst.src2);
- 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.MapReg(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 +480,9 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) {
break;
case VC_EN:
case VC_NN:
- fpr.MapReg(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 +491,9 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) {
break;
case VC_EI:
case VC_NI:
- fpr.MapReg(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 +502,9 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) {
break;
case VC_ES:
case VC_NS:
- fpr.MapReg(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 +519,15 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) {
break;
}
- ANDI(gpr.R(IRREG_VFPUL_CC), gpr.R(IRREG_VFPUL_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_VFPUL_CC), gpr.R(IRREG_VFPUL_CC), SCRATCH1);
+ OR(regs_.R(IRREG_VFPU_CC), regs_.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);
+ 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 +539,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(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 +579,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.MapReg(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 +607,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_.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.
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.MapDirtyIn(inst.dest, inst.src1);
+ 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_.MapWithFPRTemp(inst);
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 348221ef4b..08f5cb88e2 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.MapReg(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,15 @@ 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.MapRegAsPointer(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.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
+ regs_.MapGPR(inst.dest, MIPSMap::NOINIT);
+ regs_.MarkGPRDirty(inst.dest, true);
s32 imm = AdjustForAddressOffset(&addrReg, inst.constant);
@@ -89,29 +90,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 +147,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.MapRegAsPointer(inst.src1);
+ } else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) {
+ addrReg = regs_.MapGPRAsPointer(inst.src1);
} else {
SetScratch1ToSrc1Address(inst.src1);
addrReg = SCRATCH1;
@@ -159,8 +160,8 @@ void RiscVJitBackend::CompIR_FLoad(IRInst inst) {
switch (inst.op) {
case IROp::LoadFloat:
- fpr.MapReg(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 +180,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.MapRegAsPointer(inst.src1);
+ } else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) {
+ addrReg = regs_.MapGPRAsPointer(inst.src1);
} else {
SetScratch1ToSrc1Address(inst.src1);
addrReg = SCRATCH1;
@@ -195,8 +196,8 @@ 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);
- 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 +210,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 +218,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.MapRegAsPointer(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.MapReg(inst.src3);
+ valueReg = regs_.MapGPR(inst.src3);
s32 imm = AdjustForAddressOffset(&addrReg, inst.constant);
@@ -255,7 +256,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 +264,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.MapRegAsPointer(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.MapReg(inst.src3, inst.dest == MIPS_REG_ZERO ? MIPSMap::INIT : MIPSMap::DIRTY);
- gpr.MapReg(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 +318,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.MapRegAsPointer(inst.src1);
+ } else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) {
+ addrReg = regs_.MapGPRAsPointer(inst.src1);
} else {
SetScratch1ToSrc1Address(inst.src1);
addrReg = SCRATCH1;
@@ -330,8 +331,8 @@ void RiscVJitBackend::CompIR_FStore(IRInst inst) {
switch (inst.op) {
case IROp::StoreFloat:
- fpr.MapReg(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 +351,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.MapRegAsPointer(inst.src1);
+ } else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) {
+ addrReg = regs_.MapGPRAsPointer(inst.src1);
} else {
SetScratch1ToSrc1Address(inst.src1);
addrReg = SCRATCH1;
@@ -366,8 +367,8 @@ 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);
- 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 ce41e6faf0..ff6de6786f 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.MapReg(inst.dest, MIPSMap::NOINIT);
+ 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.MapIn(inst.src1);
- MovToPC(gpr.R(inst.src1));
+ regs_.Map(inst);
+ MovToPC(regs_.R(inst.src1));
break;
case IROp::SetPCConst:
@@ -90,48 +90,49 @@ 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_.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:
- gpr.MapReg(IRREG_VFPU_CTRL_BASE + inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
- fpr.MapReg(inst.src1);
- FMV(FMv::X, FMv::W, gpr.R(IRREG_VFPU_CTRL_BASE + inst.dest), fpr.R(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;
case IROp::FpCondFromReg:
- gpr.MapDirtyIn(IRREG_FPCOND, inst.src1);
- MV(gpr.R(IRREG_FPCOND), gpr.R(inst.src1));
+ regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } });
+ 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_.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:
- gpr.MapDirtyIn(IRREG_FPCOND, inst.src1, MapType::AVOID_LOAD_MARK_NORM32);
+ regs_.MapWithExtra(inst, { { 'G', IRREG_FPCOND, 1, MIPSMap::NOINIT } });
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);
+ regs_.MarkGPRDirty(IRREG_FPCOND, true);
break;
case IROp::FpCtrlToReg:
- gpr.MapDirtyIn(inst.dest, IRREG_FPCOND, MapType::AVOID_LOAD_MARK_NORM32);
+ 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) {
@@ -142,34 +143,35 @@ 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);
+ regs_.MarkGPRDirty(inst.dest, true);
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_.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;
case IROp::FMovFromGPR:
- fpr.MapReg(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);
+ 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 {
- gpr.MapReg(inst.src1);
- FMV(FMv::W, FMv::X, fpr.R(inst.dest), gpr.R(inst.src1));
+ regs_.Map(inst);
+ FMV(FMv::W, FMv::X, regs_.F(inst.dest), regs_.R(inst.src1));
}
break;
case IROp::FMovToGPR:
- gpr.MapReg(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
- fpr.MapReg(inst.src1);
- FMV(FMv::X, FMv::W, gpr.R(inst.dest), fpr.R(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/RiscVCompVec.cpp b/Core/MIPS/RiscV/RiscVCompVec.cpp
index 3e0f5c1d34..8db60a76a8 100644
--- a/Core/MIPS/RiscV/RiscVCompVec.cpp
+++ b/Core/MIPS/RiscV/RiscVCompVec.cpp
@@ -40,39 +40,36 @@ 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);
+ 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;
@@ -80,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;
@@ -90,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;
@@ -100,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;
}
@@ -110,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_.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.
@@ -124,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;
}
};
@@ -161,26 +158,26 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) {
moveChained({ neededByDepth2, neededBy, i, foundIn }, neededByDepth3 == foundIn);
}
} else {
- fpr.Map4DirtyIn(inst.dest, inst.src1);
+ 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.Map4DirtyInIn(inst.dest, inst.src1, inst.src2);
+ 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.Map4DirtyIn(inst.dest, inst.src1);
+ 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:
@@ -194,47 +191,46 @@ void RiscVJitBackend::CompIR_VecArith(IRInst inst) {
switch (inst.op) {
case IROp::Vec4Add:
- fpr.Map4DirtyInIn(inst.dest, inst.src1, inst.src2);
+ 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.Map4DirtyInIn(inst.dest, inst.src1, inst.src2);
+ 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.Map4DirtyInIn(inst.dest, inst.src1, inst.src2);
+ 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.Map4DirtyInIn(inst.dest, inst.src1, inst.src2);
+ 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.MapReg(inst.src2);
- fpr.Map4DirtyIn(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, 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.Map4DirtyIn(inst.dest, inst.src1);
+ 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.Map4DirtyIn(inst.dest, inst.src1);
+ 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:
@@ -248,33 +244,23 @@ 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);
- for (int i = 0; i < 4; ++i) {
- fpr.SpillLockFPR(inst.src1 + i);
- fpr.SpillLockFPR(inst.src2 + i);
- }
- for (int i = 0; i < 4; ++i) {
- fpr.MapReg(inst.src1 + i);
- fpr.MapReg(inst.src2 + i);
- }
- fpr.MapReg(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.
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;
@@ -295,14 +281,9 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) {
break;
case IROp::Vec4Unpack8To32:
- fpr.SpillLockFPR(inst.src1);
- for (int i = 0; i < 4; ++i)
- fpr.SpillLockFPR(inst.dest + i);
- fpr.MapReg(inst.src1);
- for (int i = 0; i < 4; ++i)
- fpr.MapReg(inst.dest + i, MIPSMap::NOINIT);
-
- FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1));
+ // 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.
if (i != 0) {
@@ -311,49 +292,39 @@ 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);
- for (int i = 0; i < 2; ++i)
- fpr.SpillLockFPR(inst.dest + i);
- fpr.MapReg(inst.src1);
- for (int i = 0; i < 2; ++i)
- fpr.MapReg(inst.dest + i, MIPSMap::NOINIT);
-
- FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1));
+ // 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, 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.Map4DirtyIn(inst.dest, inst.src1);
+ 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);
+ // TODO: This works for now, but may need to handle aliasing for vectors.
+ regs_.Map(inst);
for (int i = 0; i < 4; ++i) {
- fpr.SpillLockFPR(inst.src1 + i);
- fpr.MapReg(inst.src1 + i);
- }
- fpr.MapReg(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);
@@ -364,13 +335,14 @@ 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));
+ // 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.
SLLI(SCRATCH1, SCRATCH1, XLEN - 32);
// Now we just set (SCRATCH2 & 0xFFFF0000) | SCRATCH1.
@@ -380,7 +352,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:
@@ -394,9 +366,9 @@ void RiscVJitBackend::CompIR_VecClamp(IRInst inst) {
switch (inst.op) {
case IROp::Vec4ClampToZero:
- fpr.Map4DirtyIn(inst.dest, inst.src1);
+ 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);
@@ -404,7 +376,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..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"
@@ -32,7 +31,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 +42,7 @@ RiscVJitBackend::RiscVJitBackend(JitOptions &jitopt, IRBlockCache &blocks)
AllocCodeSpace(1024 * 1024 * 16);
SetAutoCompress(true);
- gpr.Init(this);
- fpr.Init(this);
+ regs_.Init(this);
}
RiscVJitBackend::~RiscVJitBackend() {
@@ -60,7 +58,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;
@@ -76,23 +73,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;
+ 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;
+ addresses[GetCodePtr()] = i;
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) {
@@ -135,24 +128,26 @@ 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);
+ 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;
}
}
@@ -168,7 +163,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 {
@@ -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 {
@@ -289,7 +283,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);
@@ -303,7 +297,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);
}
}
@@ -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 e364a2ab97..d8bf178363 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?
- totalNativeRegs_ = NUM_RVREG;
+ // TODO: Update these when using RISC-V V.
+ config_.totalNativeRegs = NUM_RVGPR + NUM_RVFPR;
+ config_.mapUseVRegs = false;
+ config_.mapFPUSIMD = false;
}
void RiscVRegCache::Init(RiscVEmitter *emitter) {
@@ -53,24 +55,41 @@ void RiscVRegCache::SetupInitialRegs() {
mrInitial_[MIPS_REG_ZERO].isStatic = true;
}
-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,
- };
-
+const int *RiscVRegCache::GetAllocationOrder(MIPSLoc type, MIPSMap flags, int &count, int &base) const {
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);
}
}
@@ -213,14 +236,18 @@ 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_;
}
return reg;
}
-RiscVReg RiscVRegCache::MapReg(IRReg mipsReg, MIPSMap mapFlags) {
+RiscVReg RiscVRegCache::MapWithFPRTemp(IRInst &inst) {
+ return (RiscVReg)MapWithTemp(inst, MIPSLoc::FREG);
+}
+
+RiscVReg RiscVRegCache::MapGPR(IRReg mipsReg, MIPSMap mapFlags) {
_dbg_assert_(IsValidGPR(mipsReg));
// Okay, not mapped, so we need to allocate an RV register.
@@ -228,61 +255,18 @@ 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);
-}
+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);
-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);
- 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);
- ReleaseSpillLockGPR(rd1, rd2, rs, rt);
+ IRNativeReg nreg = MapNativeReg(MIPSLoc::FREG, mipsReg + 32, 1, mapFlags);
+ if (nreg != -1)
+ return (RiscVReg)nreg;
+ return INVALID_REG;
}
void RiscVRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) {
@@ -316,34 +300,63 @@ 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 && 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) {
@@ -358,7 +371,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;
@@ -384,16 +397,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);
@@ -424,3 +427,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 9080c4c783..a11aedb09f 100644
--- a/Core/MIPS/RiscV/RiscVRegCache.h
+++ b/Core/MIPS/RiscV/RiscVRegCache.h
@@ -35,12 +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,
- AVOID_LOAD_MARK_NORM32,
- ALWAYS_LOAD,
-};
-
} // namespace RiscVJitConstants
class RiscVRegCache : public IRNativeRegCacheBase {
@@ -52,28 +46,25 @@ 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);
+ RiscVGen::RiscVReg MapFPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT);
+
+ RiscVGen::RiscVReg MapWithFPRTemp(IRInst &inst);
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);
- void DiscardR(IRReg r);
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();
@@ -82,9 +73,10 @@ 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;
+ 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;
@@ -94,6 +86,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 604cebfdfe..0000000000
--- a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp
+++ /dev/null
@@ -1,211 +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) {
- totalNativeRegs_ = NUM_RVFPUREG;
-}
-
-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::MapReg(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::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);
- 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);
- 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)
- 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)
- 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);
- for (int i = 0; i < 4; ++i)
- MapReg(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::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) {
- FlushNativeReg(i);
- }
- for (int i = 10; i <= 17; ++i) {
- FlushNativeReg(i);
- }
- for (int i = 28; i <= 31; ++i) {
- FlushNativeReg(i);
- }
-}
-
-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);
- 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 6b612053e3..0000000000
--- a/Core/MIPS/RiscV/RiscVRegCacheFPU.h
+++ /dev/null
@@ -1,65 +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 MapReg(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);
- void DiscardR(IRReg r);
-
- 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,
- };
-};
diff --git a/Core/MIPS/x86/X64IRAsm.cpp b/Core/MIPS/x86/X64IRAsm.cpp
new file mode 100644
index 0000000000..7282bc68d4
--- /dev/null
+++ b/Core/MIPS/x86/X64IRAsm.cpp
@@ -0,0 +1,267 @@
+// 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, downcountOffset), R(DOWNCOUNTREG));
+ //regs_.EmitSaveStaticRegisters();
+ RET();
+
+ loadStaticRegisters_ = AlignCode16();
+ //regs_.EmitLoadStaticRegisters();
+ //MOV(32, R(DOWNCOUNTREG), MDisp(CTXREG, downcountOffset));
+ RET();
+
+ start = saveStaticRegisters_;
+ } else {
+ saveStaticRegisters_ = nullptr;
+ loadStaticRegisters_ = nullptr;
+ }
+
+ restoreRoundingMode_ = AlignCode16();
+ {
+ STMXCSR(MDisp(CTXREG, tempOffset));
+ // Clear the rounding mode and flush-to-zero bits back to 0.
+ AND(32, MDisp(CTXREG, tempOffset), Imm32(~(7 << 13)));
+ LDMXCSR(MDisp(CTXREG, tempOffset));
+ RET();
+ }
+
+ applyRoundingMode_ = AlignCode16();
+ {
+ 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, tempOffset));
+
+ // 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, tempOffset), Imm32(~(7 << 13)));
+ OR(32, MDisp(CTXREG, tempOffset), R(SCRATCH1));
+
+ TEST(32, MDisp(CTXREG, fcr31Offset), Imm32(1 << 24));
+ FixupBranch skip3 = J_CC(CC_Z);
+ OR(32, MDisp(CTXREG, tempOffset), Imm32(1 << 15));
+ SetJumpTarget(skip3);
+
+ LDMXCSR(MDisp(CTXREG, tempOffset));
+ 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, downcountOffset), 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, downcountOffset), Imm32(0));
+ FixupBranch bail = J_CC(CC_S, true);
+ SetJumpTarget(skipToRealDispatch);
+
+ dispatcherNoCheck_ = GetCodePtr();
+
+ // Debug
+ if (enableDebug) {
+#if PPSSPP_ARCH(AMD64)
+ 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), (u32)Memory::base, (u32)GetBasePtr());
+#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/X64IRCompALU.cpp b/Core/MIPS/x86/X64IRCompALU.cpp
new file mode 100644
index 0000000000..daee4db18b
--- /dev/null
+++ b/Core/MIPS/x86/X64IRCompALU.cpp
@@ -0,0 +1,368 @@
+// 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;
+
+ 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_.SpillLockGPR(inst.dest, inst.src2);
+ 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;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+}
+
+void X64JitBackend::CompIR_Assign(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ 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);
+ 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:
+ 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);
+ 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:
+ 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;
+ }
+}
+
+} // namespace MIPSComp
+
+#endif
diff --git a/Core/MIPS/x86/X64IRCompBranch.cpp b/Core/MIPS/x86/X64IRCompBranch.cpp
new file mode 100644
index 0000000000..367336eee9
--- /dev/null
+++ b/Core/MIPS/x86/X64IRCompBranch.cpp
@@ -0,0 +1,149 @@
+// 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;
+
+ X64Reg exitReg = INVALID_REG;
+ switch (inst.op) {
+ case IROp::ExitToConst:
+ FlushAll();
+ WriteConstExit(inst.constant);
+ 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;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+}
+
+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:
+ 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:
+ 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..e9c1b9f683
--- /dev/null
+++ b/Core/MIPS/x86/X64IRCompFPU.cpp
@@ -0,0 +1,274 @@
+// 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;
+
+alignas(16) const u32 reverseQNAN[4] = { 0x803FFFFF, 0x803FFFFF, 0x803FFFFF, 0x803FFFFF };
+
+void X64JitBackend::CompIR_FArith(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ 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:
+ 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:
+ {
+ 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:
+ CompIR_Generic(inst);
+ break;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+}
+
+void X64JitBackend::CompIR_FAssign(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ 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);
+ 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..534e3a2319
--- /dev/null
+++ b/Core/MIPS/x86/X64IRCompLoadStore.cpp
@@ -0,0 +1,283 @@
+// 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;
+
+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;
+
+ switch (inst.op) {
+ case IROp::Store32Conditional:
+ CompIR_Generic(inst);
+ break;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+}
+
+void X64JitBackend::CompIR_FLoad(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ OpArg addrArg = PrepareSrc1Address(inst);
+
+ switch (inst.op) {
+ case IROp::LoadFloat:
+ regs_.MapFPR(inst.dest, MIPSMap::NOINIT);
+ MOVSS(regs_.FX(inst.dest), addrArg);
+ break;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+}
+
+void X64JitBackend::CompIR_FStore(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ OpArg addrArg = PrepareSrc1Address(inst);
+
+ switch (inst.op) {
+ case IROp::StoreFloat:
+ regs_.MapFPR(inst.src3);
+ MOVSS(addrArg, regs_.FX(inst.dest));
+ break;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+}
+
+void X64JitBackend::CompIR_Load(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ regs_.SpillLockGPR(inst.dest, inst.src1);
+ OpArg addrArg = PrepareSrc1Address(inst);
+ // 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:
+ if (inst.dest != MIPS_REG_ZERO)
+ MOV(32, regs_.R(inst.dest), addrArg);
+ regs_.SetGPRImm(IRREG_LLBIT, 1);
+ 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;
+
+ 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, valueFlags);
+ 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, MIPSMap::INIT | valueFlags));
+ }
+
+ // 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:
+ MOV(32, addrArg, valueArg);
+ 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;
+
+ OpArg addrArg = PrepareSrc1Address(inst);
+
+ switch (inst.op) {
+ case IROp::LoadVec4:
+ regs_.MapVec4(inst.dest, MIPSMap::NOINIT);
+ MOVUPS(regs_.FX(inst.dest), addrArg);
+ break;
+
+ default:
+ INVALIDOP;
+ break;
+ }
+}
+
+void X64JitBackend::CompIR_VecStore(IRInst inst) {
+ CONDITIONAL_DISABLE;
+
+ OpArg addrArg = PrepareSrc1Address(inst);
+
+ switch (inst.op) {
+ case IROp::StoreVec4:
+ regs_.MapVec4(inst.src3);
+ MOVUPS(addrArg, regs_.FX(inst.dest));
+ 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..7e1061f494
--- /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, downcountOffset), 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..cc7af5025f
--- /dev/null
+++ b/Core/MIPS/x86/X64IRCompVec.cpp
@@ -0,0 +1,134 @@
+// 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:
+ CompIR_Generic(inst);
+ break;
+
+ case IROp::Vec4Mov:
+ regs_.Map(inst);
+ MOVAPS(regs_.FX(inst.dest), regs_.F(inst.src1));
+ 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
new file mode 100644
index 0000000000..6f3d7e24d1
--- /dev/null
+++ b/Core/MIPS/x86/X64IRJit.cpp
@@ -0,0 +1,331 @@
+// 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, downcountOffset), 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()] = i;
+
+ 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, downcountOffset), 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 (%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), inst);
+ INFO_LOG(JIT, "IR: #%d %s", it->second, 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, pcOffset));
+}
+
+void X64JitBackend::MovToPC(X64Reg r) {
+ MOV(32, MDisp(CTXREG, pcOffset), R(r));
+}
+
+void X64JitBackend::SaveStaticRegisters() {
+ if (jo.useStaticAlloc) {
+ //CALL(saveStaticRegisters_);
+ } else {
+ // Inline the single operation
+ //MOV(32, MDisp(CTXREG, downcountOffset), R(DOWNCOUNTREG));
+ }
+}
+
+void X64JitBackend::LoadStaticRegisters() {
+ if (jo.useStaticAlloc) {
+ //CALL(loadStaticRegisters_);
+ } else {
+ //MOV(32, R(DOWNCOUNTREG), MDisp(CTXREG, downcountOffset));
+ }
+}
+
+} // namespace MIPSComp
+
+#endif
diff --git a/Core/MIPS/x86/X64IRJit.h b/Core/MIPS/x86/X64IRJit.h
new file mode 100644
index 0000000000..3b5189398e
--- /dev/null
+++ b/Core/MIPS/x86/X64IRJit.h
@@ -0,0 +1,143 @@
+// 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;
+
+ Gen::OpArg PrepareSrc1Address(IRInst inst);
+
+ 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..07c1be33a3
--- /dev/null
+++ b/Core/MIPS/x86/X64IRRegCache.cpp
@@ -0,0 +1,373 @@
+// 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) {
+ config_.totalNativeRegs = NUM_X_REGS + NUM_X_FREGS;
+ config_.mapFPUSIMD = true;
+ // 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, MIPSMap flags, 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
+ // Intentionally last.
+ R15,
+#elif PPSSPP_ARCH(X86)
+ ESI, EDI, EDX, EBX, ECX,
+#endif
+ };
+
+#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;
+ }
+#endif
+ 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, XMM1, XMM2, XMM3, XMM4, XMM5, XMM0,
+#elif PPSSPP_ARCH(X86)
+ XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM0,
+#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, 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)) {
+ if (canUseReg(RX(r)))
+ return RX(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.
+ if (canUseReg(FromNativeReg(mr[i].nReg)))
+ return FromNativeReg(mr[i].nReg);
+ }
+ }
+ }
+
+ return INVALID_REG;
+}
+
+X64Reg X64IRRegCache::GetAndLockTempR() {
+ X64Reg reg = FromNativeReg(AllocateReg(MIPSLoc::REG, MIPSMap::INIT));
+ 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;
+}
+
+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);
+ 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));
+}
+
+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));
+}
+
+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) {
+ return FromNativeReg(mr[mipsReg].nReg);
+ } else {
+ ERROR_LOG_REPORT(JIT, "Reg %i not in x64 reg", mipsReg);
+ return INVALID_REG; // BAAAD
+ }
+}
+
+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) {
+ 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::FX(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
+ }
+}
+
+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
new file mode 100644
index 0000000000..e4d3bdb592
--- /dev/null
+++ b/Core/MIPS/x86/X64IRRegCache.h
@@ -0,0 +1,128 @@
+// 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;
+
+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;
+
+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 {
+public:
+ X64IRRegCache(MIPSComp::JitOptions *jo);
+
+ void Init(Gen::XEmitter *emitter);
+
+ // May fail and return INVALID_REG if it needs flushing.
+ 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);
+ 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);
+
+ void FlushBeforeCall();
+
+ 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
+ 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;
+
+ 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
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..9120724fc7 100644
--- a/UWP/CoreUWP/CoreUWP.vcxproj
+++ b/UWP/CoreUWP/CoreUWP.vcxproj
@@ -304,6 +304,8 @@
+
+
@@ -573,6 +575,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters
index 3d49ff54fc..91839d6316 100644
--- a/UWP/CoreUWP/CoreUWP.vcxproj.filters
+++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters
@@ -189,6 +189,33 @@
MIPS\x86
+
+ MIPS\x86
+
+
+ MIPS\x86
+
+
+ MIPS\x86
+
+
+ MIPS\x86
+
+
+ MIPS\x86
+
+
+ MIPS\x86
+
+
+ MIPS\x86
+
+
+ MIPS\x86
+
+
+ MIPS\x86
+
FileSystems
@@ -1200,6 +1227,12 @@
MIPS\x86
+
+ MIPS\x86
+
+
+ MIPS\x86
+
FileSystems
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index b98569a2d5..95e98a34fb 100644
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -303,6 +303,15 @@ 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/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 \
$(SRC)/GPU/Software/DrawPixelX86.cpp \
$(SRC)/GPU/Software/SamplerX86.cpp
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 = 메모리 페이지 크기
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..f7c2c98a29 100644
--- a/libretro/Makefile.common
+++ b/libretro/Makefile.common
@@ -802,6 +802,15 @@ 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/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
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;