mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
riscv: Use a single reg cache.
This commit is contained in:
@@ -1633,8 +1633,6 @@ list(APPEND CoreExtra
|
||||
Core/MIPS/RiscV/RiscVJit.h
|
||||
Core/MIPS/RiscV/RiscVRegCache.cpp
|
||||
Core/MIPS/RiscV/RiscVRegCache.h
|
||||
Core/MIPS/RiscV/RiscVRegCacheFPU.cpp
|
||||
Core/MIPS/RiscV/RiscVRegCacheFPU.h
|
||||
GPU/Common/VertexDecoderRiscV.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -605,7 +605,6 @@
|
||||
<ClCompile Include="MIPS\RiscV\RiscVCompVec.cpp" />
|
||||
<ClCompile Include="MIPS\RiscV\RiscVJit.cpp" />
|
||||
<ClCompile Include="MIPS\RiscV\RiscVRegCache.cpp" />
|
||||
<ClCompile Include="MIPS\RiscV\RiscVRegCacheFPU.cpp" />
|
||||
<ClCompile Include="Replay.cpp" />
|
||||
<ClCompile Include="Compatibility.cpp" />
|
||||
<ClCompile Include="Config.cpp" />
|
||||
@@ -1179,7 +1178,6 @@
|
||||
<ClInclude Include="MIPS\MIPSVFPUFallbacks.h" />
|
||||
<ClInclude Include="MIPS\RiscV\RiscVJit.h" />
|
||||
<ClInclude Include="MIPS\RiscV\RiscVRegCache.h" />
|
||||
<ClInclude Include="MIPS\RiscV\RiscVRegCacheFPU.h" />
|
||||
<ClInclude Include="Replay.h" />
|
||||
<ClInclude Include="Compatibility.h" />
|
||||
<ClInclude Include="Config.h" />
|
||||
|
||||
@@ -1216,9 +1216,6 @@
|
||||
<ClCompile Include="MIPS\RiscV\RiscVRegCache.cpp">
|
||||
<Filter>MIPS\RiscV</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\RiscV\RiscVRegCacheFPU.cpp">
|
||||
<Filter>MIPS\RiscV</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MIPS\RiscV\RiscVCompFPU.cpp">
|
||||
<Filter>MIPS\RiscV</Filter>
|
||||
</ClCompile>
|
||||
@@ -1998,9 +1995,6 @@
|
||||
<ClInclude Include="MIPS\RiscV\RiscVRegCache.h">
|
||||
<Filter>MIPS\RiscV</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MIPS\RiscV\RiscVRegCacheFPU.h">
|
||||
<Filter>MIPS\RiscV</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MIPS\IR\IRAnalysis.h">
|
||||
<Filter>MIPS\IR</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -563,12 +563,17 @@ void IRNativeRegCacheBase::FlushReg(IRReg mreg) {
|
||||
}
|
||||
}
|
||||
|
||||
void IRNativeRegCacheBase::FlushAll() {
|
||||
void IRNativeRegCacheBase::FlushAll(bool gprs, bool fprs) {
|
||||
// Note: make sure not to change the registers when flushing.
|
||||
// Branching code may expect the native reg to retain its value.
|
||||
|
||||
for (int i = 1; i < TOTAL_MAPPABLE_IRREGS; i++) {
|
||||
IRReg mipsReg = (IRReg)i;
|
||||
if (!fprs && i >= 32 && IsValidFPR(mipsReg))
|
||||
continue;
|
||||
if (!gprs && IsValidGPR(mipsReg))
|
||||
continue;
|
||||
|
||||
if (mr[i].isStatic) {
|
||||
IRNativeReg nreg = mr[i].nReg;
|
||||
// Cannot leave any IMMs in registers, not even MIPSLoc::REG_IMM.
|
||||
@@ -599,6 +604,10 @@ void IRNativeRegCacheBase::FlushAll() {
|
||||
int count = 0;
|
||||
const StaticAllocation *allocs = GetStaticAllocations(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!fprs && allocs[i].loc != MIPSLoc::FREG && allocs[i].loc != MIPSLoc::VREG)
|
||||
continue;
|
||||
if (!gprs && allocs[i].loc != MIPSLoc::REG)
|
||||
continue;
|
||||
if (allocs[i].pointerified && !nr[allocs[i].nr].pointerified && jo_->enablePointerify) {
|
||||
// Re-pointerify
|
||||
if (mr[allocs[i].mr].loc == MIPSLoc::REG_IMM)
|
||||
|
||||
@@ -184,7 +184,7 @@ public:
|
||||
void MarkGPRAsPointerDirty(IRReg gpr);
|
||||
|
||||
virtual void Map(const IRInst &inst);
|
||||
virtual void FlushAll();
|
||||
virtual void FlushAll(bool gprs = true, bool fprs = true);
|
||||
|
||||
protected:
|
||||
virtual void SetupInitialRegs();
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
+204
-204
@@ -53,47 +53,47 @@ void RiscVJitBackend::CompIR_Arith(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Add:
|
||||
gpr.Map(inst);
|
||||
ADDW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.Map(inst);
|
||||
ADDW(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
break;
|
||||
|
||||
case IROp::Sub:
|
||||
gpr.Map(inst);
|
||||
SUBW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.Map(inst);
|
||||
SUBW(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
break;
|
||||
|
||||
case IROp::AddConst:
|
||||
if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) {
|
||||
// Typical of stack pointer updates.
|
||||
if (gpr.IsGPRMappedAsPointer(inst.dest) && inst.dest == inst.src1 && allowPtrMath) {
|
||||
gpr.MarkGPRAsPointerDirty(inst.dest);
|
||||
ADDI(gpr.RPtr(inst.dest), gpr.RPtr(inst.dest), inst.constant);
|
||||
if (regs_.IsGPRMappedAsPointer(inst.dest) && inst.dest == inst.src1 && allowPtrMath) {
|
||||
regs_.MarkGPRAsPointerDirty(inst.dest);
|
||||
ADDI(regs_.RPtr(inst.dest), regs_.RPtr(inst.dest), inst.constant);
|
||||
} else {
|
||||
gpr.Map(inst);
|
||||
ADDIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant);
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.Map(inst);
|
||||
ADDIW(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
}
|
||||
} else {
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
LI(SCRATCH1, (int32_t)inst.constant);
|
||||
ADDW(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1);
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
ADDW(regs_.R(inst.dest), regs_.R(inst.src1), SCRATCH1);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::SubConst:
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
LI(SCRATCH1, (int32_t)inst.constant);
|
||||
SUBW(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1);
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
SUBW(regs_.R(inst.dest), regs_.R(inst.src1), SCRATCH1);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
break;
|
||||
|
||||
case IROp::Neg:
|
||||
gpr.Map(inst);
|
||||
SUBW(gpr.R(inst.dest), R_ZERO, gpr.R(inst.src1));
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.Map(inst);
|
||||
SUBW(regs_.R(inst.dest), R_ZERO, regs_.R(inst.src1));
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -109,81 +109,81 @@ void RiscVJitBackend::CompIR_Logic(IRInst inst) {
|
||||
switch (inst.op) {
|
||||
case IROp::And:
|
||||
if (inst.src1 != inst.src2) {
|
||||
gpr.Map(inst);
|
||||
AND(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
|
||||
regs_.Map(inst);
|
||||
AND(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));
|
||||
} else if (inst.src1 != inst.dest) {
|
||||
gpr.Map(inst);
|
||||
MV(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1));
|
||||
regs_.Map(inst);
|
||||
MV(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Or:
|
||||
if (inst.src1 != inst.src2) {
|
||||
// If both were normalized before, the result is normalized.
|
||||
resultNormalized = gpr.IsNormalized32(inst.src1) && gpr.IsNormalized32(inst.src2);
|
||||
gpr.Map(inst);
|
||||
OR(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
|
||||
gpr.MarkGPRDirty(inst.dest, resultNormalized);
|
||||
resultNormalized = regs_.IsNormalized32(inst.src1) && regs_.IsNormalized32(inst.src2);
|
||||
regs_.Map(inst);
|
||||
OR(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));
|
||||
regs_.MarkGPRDirty(inst.dest, resultNormalized);
|
||||
} else if (inst.src1 != inst.dest) {
|
||||
gpr.Map(inst);
|
||||
MV(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1));
|
||||
regs_.Map(inst);
|
||||
MV(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Xor:
|
||||
if (inst.src1 == inst.src2) {
|
||||
gpr.SetGPRImm(inst.dest, 0);
|
||||
regs_.SetGPRImm(inst.dest, 0);
|
||||
} else {
|
||||
gpr.Map(inst);
|
||||
XOR(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
|
||||
regs_.Map(inst);
|
||||
XOR(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::AndConst:
|
||||
resultNormalized = gpr.IsNormalized32(inst.src1);
|
||||
gpr.Map(inst);
|
||||
resultNormalized = regs_.IsNormalized32(inst.src1);
|
||||
regs_.Map(inst);
|
||||
if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) {
|
||||
ANDI(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant);
|
||||
ANDI(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant);
|
||||
} else {
|
||||
LI(SCRATCH1, (int32_t)inst.constant);
|
||||
AND(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1);
|
||||
AND(regs_.R(inst.dest), regs_.R(inst.src1), SCRATCH1);
|
||||
}
|
||||
// If the sign bits aren't cleared, and it was normalized before - it still is.
|
||||
if ((inst.constant & 0x80000000) != 0 && resultNormalized)
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
// Otherwise, if we cleared the sign bits, it's naturally normalized.
|
||||
else if ((inst.constant & 0x80000000) == 0)
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
break;
|
||||
|
||||
case IROp::OrConst:
|
||||
resultNormalized = gpr.IsNormalized32(inst.src1);
|
||||
gpr.Map(inst);
|
||||
resultNormalized = regs_.IsNormalized32(inst.src1);
|
||||
regs_.Map(inst);
|
||||
if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) {
|
||||
ORI(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant);
|
||||
ORI(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant);
|
||||
} else {
|
||||
LI(SCRATCH1, (int32_t)inst.constant);
|
||||
OR(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1);
|
||||
OR(regs_.R(inst.dest), regs_.R(inst.src1), SCRATCH1);
|
||||
}
|
||||
// Since our constant is normalized, oring its bits in won't hurt normalization.
|
||||
gpr.MarkGPRDirty(inst.dest, resultNormalized);
|
||||
regs_.MarkGPRDirty(inst.dest, resultNormalized);
|
||||
break;
|
||||
|
||||
case IROp::XorConst:
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) {
|
||||
XORI(gpr.R(inst.dest), gpr.R(inst.src1), inst.constant);
|
||||
XORI(regs_.R(inst.dest), regs_.R(inst.src1), inst.constant);
|
||||
} else {
|
||||
LI(SCRATCH1, (int32_t)inst.constant);
|
||||
XOR(gpr.R(inst.dest), gpr.R(inst.src1), SCRATCH1);
|
||||
XOR(regs_.R(inst.dest), regs_.R(inst.src1), SCRATCH1);
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Not:
|
||||
gpr.Map(inst);
|
||||
NOT(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
regs_.Map(inst);
|
||||
NOT(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -198,32 +198,32 @@ void RiscVJitBackend::CompIR_Assign(IRInst inst) {
|
||||
switch (inst.op) {
|
||||
case IROp::Mov:
|
||||
if (inst.dest != inst.src1) {
|
||||
gpr.Map(inst);
|
||||
MV(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1));
|
||||
regs_.Map(inst);
|
||||
MV(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Ext8to32:
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
if (cpu_info.RiscV_Zbb) {
|
||||
SEXT_B(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
SEXT_B(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
} else {
|
||||
SLLI(gpr.R(inst.dest), gpr.R(inst.src1), 24);
|
||||
SRAIW(gpr.R(inst.dest), gpr.R(inst.dest), 24);
|
||||
SLLI(regs_.R(inst.dest), regs_.R(inst.src1), 24);
|
||||
SRAIW(regs_.R(inst.dest), regs_.R(inst.dest), 24);
|
||||
}
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
break;
|
||||
|
||||
case IROp::Ext16to32:
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
if (cpu_info.RiscV_Zbb) {
|
||||
SEXT_H(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
SEXT_H(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
} else {
|
||||
SLLI(gpr.R(inst.dest), gpr.R(inst.src1), 16);
|
||||
SRAIW(gpr.R(inst.dest), gpr.R(inst.dest), 16);
|
||||
SLLI(regs_.R(inst.dest), regs_.R(inst.src1), 16);
|
||||
SRAIW(regs_.R(inst.dest), regs_.R(inst.dest), 16);
|
||||
}
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -238,37 +238,37 @@ void RiscVJitBackend::CompIR_Bits(IRInst inst) {
|
||||
switch (inst.op) {
|
||||
case IROp::ReverseBits:
|
||||
if (cpu_info.RiscV_Zbb) {
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
// Start by reversing bytes (note: this puts in upper 32 of XLEN.)
|
||||
REV8(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
REV8(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
|
||||
// Swap nibbles.
|
||||
LI(SCRATCH1, (s32)0xF0F0F0F0);
|
||||
SRLI(SCRATCH2, gpr.R(inst.dest), XLEN - 32 - 4);
|
||||
SRLI(SCRATCH2, regs_.R(inst.dest), XLEN - 32 - 4);
|
||||
AND(SCRATCH2, SCRATCH2, SCRATCH1);
|
||||
if (XLEN >= 64)
|
||||
SRLI(gpr.R(inst.dest), gpr.R(inst.dest), XLEN - 28);
|
||||
SRLI(regs_.R(inst.dest), regs_.R(inst.dest), XLEN - 28);
|
||||
else
|
||||
SLLI(gpr.R(inst.dest), gpr.R(inst.dest), 4);
|
||||
SLLI(regs_.R(inst.dest), regs_.R(inst.dest), 4);
|
||||
SRLIW(SCRATCH1, SCRATCH1, 4);
|
||||
AND(gpr.R(inst.dest), gpr.R(inst.dest), SCRATCH1);
|
||||
OR(gpr.R(inst.dest), gpr.R(inst.dest), SCRATCH2);
|
||||
AND(regs_.R(inst.dest), regs_.R(inst.dest), SCRATCH1);
|
||||
OR(regs_.R(inst.dest), regs_.R(inst.dest), SCRATCH2);
|
||||
|
||||
// Now the consecutive pairs.
|
||||
LI(SCRATCH1, (s32)0x33333333);
|
||||
SRLI(SCRATCH2, gpr.R(inst.dest), 2);
|
||||
SRLI(SCRATCH2, regs_.R(inst.dest), 2);
|
||||
AND(SCRATCH2, SCRATCH2, SCRATCH1);
|
||||
AND(gpr.R(inst.dest), gpr.R(inst.dest), SCRATCH1);
|
||||
SLLIW(gpr.R(inst.dest), gpr.R(inst.dest), 2);
|
||||
OR(gpr.R(inst.dest), gpr.R(inst.dest), SCRATCH2);
|
||||
AND(regs_.R(inst.dest), regs_.R(inst.dest), SCRATCH1);
|
||||
SLLIW(regs_.R(inst.dest), regs_.R(inst.dest), 2);
|
||||
OR(regs_.R(inst.dest), regs_.R(inst.dest), SCRATCH2);
|
||||
|
||||
// And finally the even and odd bits.
|
||||
LI(SCRATCH1, (s32)0x55555555);
|
||||
SRLI(SCRATCH2, gpr.R(inst.dest), 1);
|
||||
SRLI(SCRATCH2, regs_.R(inst.dest), 1);
|
||||
AND(SCRATCH2, SCRATCH2, SCRATCH1);
|
||||
AND(gpr.R(inst.dest), gpr.R(inst.dest), SCRATCH1);
|
||||
SLLIW(gpr.R(inst.dest), gpr.R(inst.dest), 1);
|
||||
OR(gpr.R(inst.dest), gpr.R(inst.dest), SCRATCH2);
|
||||
AND(regs_.R(inst.dest), regs_.R(inst.dest), SCRATCH1);
|
||||
SLLIW(regs_.R(inst.dest), regs_.R(inst.dest), 1);
|
||||
OR(regs_.R(inst.dest), regs_.R(inst.dest), SCRATCH2);
|
||||
} else {
|
||||
CompIR_Generic(inst);
|
||||
}
|
||||
@@ -280,12 +280,12 @@ void RiscVJitBackend::CompIR_Bits(IRInst inst) {
|
||||
|
||||
case IROp::BSwap32:
|
||||
if (cpu_info.RiscV_Zbb) {
|
||||
gpr.Map(inst);
|
||||
REV8(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
regs_.Map(inst);
|
||||
REV8(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
if (XLEN >= 64) {
|
||||
// REV8 swaps the entire register, so get the 32 highest bits.
|
||||
SRAI(gpr.R(inst.dest), gpr.R(inst.dest), XLEN - 32);
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
SRAI(regs_.R(inst.dest), regs_.R(inst.dest), XLEN - 32);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
}
|
||||
} else {
|
||||
CompIR_Generic(inst);
|
||||
@@ -294,10 +294,10 @@ void RiscVJitBackend::CompIR_Bits(IRInst inst) {
|
||||
|
||||
case IROp::Clz:
|
||||
if (cpu_info.RiscV_Zbb) {
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
// This even sets to 32 when zero, perfect.
|
||||
CLZW(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
CLZW(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
} else {
|
||||
CompIR_Generic(inst);
|
||||
}
|
||||
@@ -314,28 +314,28 @@ void RiscVJitBackend::CompIR_Shift(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Shl:
|
||||
gpr.Map(inst);
|
||||
SLLW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.Map(inst);
|
||||
SLLW(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
break;
|
||||
|
||||
case IROp::Shr:
|
||||
gpr.Map(inst);
|
||||
SRLW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.Map(inst);
|
||||
SRLW(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
break;
|
||||
|
||||
case IROp::Sar:
|
||||
gpr.Map(inst);
|
||||
SRAW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.Map(inst);
|
||||
SRAW(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
break;
|
||||
|
||||
case IROp::Ror:
|
||||
if (cpu_info.RiscV_Zbb) {
|
||||
gpr.Map(inst);
|
||||
RORW(gpr.R(inst.dest), gpr.R(inst.src1), gpr.R(inst.src2));
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.Map(inst);
|
||||
RORW(regs_.R(inst.dest), regs_.R(inst.src1), regs_.R(inst.src2));
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
} else {
|
||||
CompIR_Generic(inst);
|
||||
}
|
||||
@@ -344,67 +344,67 @@ void RiscVJitBackend::CompIR_Shift(IRInst inst) {
|
||||
case IROp::ShlImm:
|
||||
// Shouldn't happen, but let's be safe of any passes that modify the ops.
|
||||
if (inst.src2 >= 32) {
|
||||
gpr.SetGPRImm(inst.dest, 0);
|
||||
regs_.SetGPRImm(inst.dest, 0);
|
||||
} else if (inst.src2 == 0) {
|
||||
if (inst.dest != inst.src1) {
|
||||
gpr.Map(inst);
|
||||
MV(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1));
|
||||
regs_.Map(inst);
|
||||
MV(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1));
|
||||
}
|
||||
} else {
|
||||
gpr.Map(inst);
|
||||
SLLIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2);
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.Map(inst);
|
||||
SLLIW(regs_.R(inst.dest), regs_.R(inst.src1), inst.src2);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::ShrImm:
|
||||
// Shouldn't happen, but let's be safe of any passes that modify the ops.
|
||||
if (inst.src2 >= 32) {
|
||||
gpr.SetGPRImm(inst.dest, 0);
|
||||
regs_.SetGPRImm(inst.dest, 0);
|
||||
} else if (inst.src2 == 0) {
|
||||
if (inst.dest != inst.src1) {
|
||||
gpr.Map(inst);
|
||||
MV(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1));
|
||||
regs_.Map(inst);
|
||||
MV(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1));
|
||||
}
|
||||
} else {
|
||||
gpr.Map(inst);
|
||||
SRLIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2);
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.Map(inst);
|
||||
SRLIW(regs_.R(inst.dest), regs_.R(inst.src1), inst.src2);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::SarImm:
|
||||
// Shouldn't happen, but let's be safe of any passes that modify the ops.
|
||||
if (inst.src2 >= 32) {
|
||||
gpr.Map(inst);
|
||||
SRAIW(gpr.R(inst.dest), gpr.R(inst.src1), 31);
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.Map(inst);
|
||||
SRAIW(regs_.R(inst.dest), regs_.R(inst.src1), 31);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
} else if (inst.src2 == 0) {
|
||||
if (inst.dest != inst.src1) {
|
||||
gpr.Map(inst);
|
||||
MV(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1));
|
||||
regs_.Map(inst);
|
||||
MV(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1));
|
||||
}
|
||||
} else {
|
||||
gpr.Map(inst);
|
||||
SRAIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2);
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.Map(inst);
|
||||
SRAIW(regs_.R(inst.dest), regs_.R(inst.src1), inst.src2);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::RorImm:
|
||||
if (inst.src2 == 0) {
|
||||
if (inst.dest != inst.src1) {
|
||||
gpr.Map(inst);
|
||||
MV(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1));
|
||||
regs_.Map(inst);
|
||||
MV(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1));
|
||||
}
|
||||
} else if (cpu_info.RiscV_Zbb) {
|
||||
gpr.Map(inst);
|
||||
RORIW(gpr.R(inst.dest), gpr.R(inst.src1), inst.src2 & 31);
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.Map(inst);
|
||||
RORIW(regs_.R(inst.dest), regs_.R(inst.src1), inst.src2 & 31);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
} else {
|
||||
CompIR_Generic(inst);
|
||||
}
|
||||
@@ -423,58 +423,58 @@ void RiscVJitBackend::CompIR_Compare(IRInst inst) {
|
||||
RiscVReg rhs = INVALID_REG;
|
||||
switch (inst.op) {
|
||||
case IROp::Slt:
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true);
|
||||
|
||||
SLT(gpr.R(inst.dest), lhs, rhs);
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
SLT(regs_.R(inst.dest), lhs, rhs);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
break;
|
||||
|
||||
case IROp::SltConst:
|
||||
if (inst.constant == 0) {
|
||||
// Basically, getting the sign bit. Let's shift instead.
|
||||
gpr.Map(inst);
|
||||
SRLIW(gpr.R(inst.dest), gpr.R(inst.src1), 31);
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.Map(inst);
|
||||
SRLIW(regs_.R(inst.dest), regs_.R(inst.src1), 31);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
} else {
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
NormalizeSrc1(inst, &lhs, SCRATCH1, false);
|
||||
|
||||
if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) {
|
||||
SLTI(gpr.R(inst.dest), lhs, (int32_t)inst.constant);
|
||||
SLTI(regs_.R(inst.dest), lhs, (int32_t)inst.constant);
|
||||
} else {
|
||||
LI(SCRATCH2, (int32_t)inst.constant);
|
||||
SLT(gpr.R(inst.dest), lhs, SCRATCH2);
|
||||
SLT(regs_.R(inst.dest), lhs, SCRATCH2);
|
||||
}
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::SltU:
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
// It's still fine to sign extend, the biggest just get even bigger.
|
||||
NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true);
|
||||
|
||||
SLTU(gpr.R(inst.dest), lhs, rhs);
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
SLTU(regs_.R(inst.dest), lhs, rhs);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
break;
|
||||
|
||||
case IROp::SltUConst:
|
||||
if (inst.constant == 0) {
|
||||
gpr.SetGPRImm(inst.dest, 0);
|
||||
regs_.SetGPRImm(inst.dest, 0);
|
||||
} else {
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
NormalizeSrc1(inst, &lhs, SCRATCH1, false);
|
||||
|
||||
// We sign extend because we're comparing against something normalized.
|
||||
// It's also the most efficient to set.
|
||||
if ((int32_t)inst.constant >= -2048 && (int32_t)inst.constant <= 2047) {
|
||||
SLTIU(gpr.R(inst.dest), lhs, (int32_t)inst.constant);
|
||||
SLTIU(regs_.R(inst.dest), lhs, (int32_t)inst.constant);
|
||||
} else {
|
||||
LI(SCRATCH2, (int32_t)inst.constant);
|
||||
SLTU(gpr.R(inst.dest), lhs, SCRATCH2);
|
||||
SLTU(regs_.R(inst.dest), lhs, SCRATCH2);
|
||||
}
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -497,7 +497,7 @@ void RiscVJitBackend::CompIR_CondAssign(IRInst inst) {
|
||||
return;
|
||||
|
||||
// We could have a "zero" with wrong upper due to XOR, so we have to normalize.
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
NormalizeSrc1(inst, &lhs, SCRATCH1, true);
|
||||
|
||||
switch (inst.op) {
|
||||
@@ -512,43 +512,43 @@ void RiscVJitBackend::CompIR_CondAssign(IRInst inst) {
|
||||
break;
|
||||
}
|
||||
|
||||
MV(gpr.R(inst.dest), gpr.R(inst.src2));
|
||||
MV(regs_.R(inst.dest), regs_.R(inst.src2));
|
||||
SetJumpTarget(fixup);
|
||||
break;
|
||||
|
||||
case IROp::Max:
|
||||
if (inst.src1 != inst.src2) {
|
||||
if (cpu_info.RiscV_Zbb) {
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true);
|
||||
MAX(gpr.R(inst.dest), lhs, rhs);
|
||||
MAX(regs_.R(inst.dest), lhs, rhs);
|
||||
// Because we had to normalize the inputs, the output is normalized.
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
} else {
|
||||
CompIR_Generic(inst);
|
||||
}
|
||||
} else if (inst.dest != inst.src1) {
|
||||
gpr.Map(inst);
|
||||
MV(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1));
|
||||
regs_.Map(inst);
|
||||
MV(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Min:
|
||||
if (inst.src1 != inst.src2) {
|
||||
if (cpu_info.RiscV_Zbb) {
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true);
|
||||
MIN(gpr.R(inst.dest), lhs, rhs);
|
||||
MIN(regs_.R(inst.dest), lhs, rhs);
|
||||
// Because we had to normalize the inputs, the output is normalized.
|
||||
gpr.MarkGPRDirty(inst.dest, true);
|
||||
regs_.MarkGPRDirty(inst.dest, true);
|
||||
} else {
|
||||
CompIR_Generic(inst);
|
||||
}
|
||||
} else if (inst.dest != inst.src1) {
|
||||
gpr.Map(inst);
|
||||
MV(gpr.R(inst.dest), gpr.R(inst.src1));
|
||||
gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(inst.src1));
|
||||
regs_.Map(inst);
|
||||
MV(regs_.R(inst.dest), regs_.R(inst.src1));
|
||||
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(inst.src1));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -563,27 +563,27 @@ void RiscVJitBackend::CompIR_HiLo(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::MtLo:
|
||||
gpr.MapDirtyIn(IRREG_LO, inst.src1);
|
||||
MV(gpr.R(IRREG_LO), gpr.R(inst.src1));
|
||||
gpr.MarkGPRDirty(IRREG_LO, gpr.IsNormalized32(inst.src1));
|
||||
regs_.MapGPRDirtyIn(IRREG_LO, inst.src1);
|
||||
MV(regs_.R(IRREG_LO), regs_.R(inst.src1));
|
||||
regs_.MarkGPRDirty(IRREG_LO, regs_.IsNormalized32(inst.src1));
|
||||
break;
|
||||
|
||||
case IROp::MtHi:
|
||||
gpr.MapDirtyIn(IRREG_HI, inst.src1);
|
||||
MV(gpr.R(IRREG_HI), gpr.R(inst.src1));
|
||||
gpr.MarkGPRDirty(IRREG_HI, gpr.IsNormalized32(inst.src1));
|
||||
regs_.MapGPRDirtyIn(IRREG_HI, inst.src1);
|
||||
MV(regs_.R(IRREG_HI), regs_.R(inst.src1));
|
||||
regs_.MarkGPRDirty(IRREG_HI, regs_.IsNormalized32(inst.src1));
|
||||
break;
|
||||
|
||||
case IROp::MfLo:
|
||||
gpr.MapDirtyIn(inst.dest, IRREG_LO);
|
||||
MV(gpr.R(inst.dest), gpr.R(IRREG_LO));
|
||||
gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(IRREG_LO));
|
||||
regs_.MapGPRDirtyIn(inst.dest, IRREG_LO);
|
||||
MV(regs_.R(inst.dest), regs_.R(IRREG_LO));
|
||||
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_LO));
|
||||
break;
|
||||
|
||||
case IROp::MfHi:
|
||||
gpr.MapDirtyIn(inst.dest, IRREG_HI);
|
||||
MV(gpr.R(inst.dest), gpr.R(IRREG_HI));
|
||||
gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(IRREG_HI));
|
||||
regs_.MapGPRDirtyIn(inst.dest, IRREG_HI);
|
||||
MV(regs_.R(inst.dest), regs_.R(IRREG_HI));
|
||||
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_HI));
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -597,12 +597,12 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) {
|
||||
|
||||
auto makeArgsUnsigned = [&](RiscVReg *lhs, RiscVReg *rhs) {
|
||||
if (cpu_info.RiscV_Zba) {
|
||||
ZEXT_W(SCRATCH1, gpr.R(inst.src1));
|
||||
ZEXT_W(SCRATCH2, gpr.R(inst.src2));
|
||||
ZEXT_W(SCRATCH1, regs_.R(inst.src1));
|
||||
ZEXT_W(SCRATCH2, regs_.R(inst.src2));
|
||||
} else {
|
||||
SLLI(SCRATCH1, gpr.R(inst.src1), XLEN - 32);
|
||||
SLLI(SCRATCH1, regs_.R(inst.src1), XLEN - 32);
|
||||
SRLI(SCRATCH1, SCRATCH1, XLEN - 32);
|
||||
SLLI(SCRATCH2, gpr.R(inst.src2), XLEN - 32);
|
||||
SLLI(SCRATCH2, regs_.R(inst.src2), XLEN - 32);
|
||||
SRLI(SCRATCH2, SCRATCH2, XLEN - 32);
|
||||
}
|
||||
*lhs = SCRATCH1;
|
||||
@@ -611,17 +611,17 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) {
|
||||
auto combinePrevMulResult = [&] {
|
||||
// TODO: Using a single reg for HI/LO would make this less ugly.
|
||||
if (cpu_info.RiscV_Zba) {
|
||||
ZEXT_W(gpr.R(IRREG_LO), gpr.R(IRREG_LO));
|
||||
ZEXT_W(regs_.R(IRREG_LO), regs_.R(IRREG_LO));
|
||||
} else {
|
||||
SLLI(gpr.R(IRREG_LO), gpr.R(IRREG_LO), XLEN - 32);
|
||||
SRLI(gpr.R(IRREG_LO), gpr.R(IRREG_LO), XLEN - 32);
|
||||
SLLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), XLEN - 32);
|
||||
SRLI(regs_.R(IRREG_LO), regs_.R(IRREG_LO), XLEN - 32);
|
||||
}
|
||||
SLLI(gpr.R(IRREG_HI), gpr.R(IRREG_HI), 32);
|
||||
OR(gpr.R(IRREG_LO), gpr.R(IRREG_LO), gpr.R(IRREG_HI));
|
||||
SLLI(regs_.R(IRREG_HI), regs_.R(IRREG_HI), 32);
|
||||
OR(regs_.R(IRREG_LO), regs_.R(IRREG_LO), regs_.R(IRREG_HI));
|
||||
};
|
||||
auto splitMulResult = [&] {
|
||||
SRAI(gpr.R(IRREG_HI), gpr.R(IRREG_LO), 32);
|
||||
gpr.MarkGPRDirty(IRREG_HI, true);
|
||||
SRAI(regs_.R(IRREG_HI), regs_.R(IRREG_LO), 32);
|
||||
regs_.MarkGPRDirty(IRREG_HI, true);
|
||||
};
|
||||
|
||||
RiscVReg lhs = INVALID_REG;
|
||||
@@ -630,58 +630,58 @@ void RiscVJitBackend::CompIR_Mult(IRInst inst) {
|
||||
case IROp::Mult:
|
||||
// TODO: Maybe IR could simplify when HI is not needed or clobbered?
|
||||
// TODO: HI/LO merge optimization? Have to be careful of passes that split them...
|
||||
gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2);
|
||||
regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2);
|
||||
NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true);
|
||||
MUL(gpr.R(IRREG_LO), lhs, rhs);
|
||||
MUL(regs_.R(IRREG_LO), lhs, rhs);
|
||||
splitMulResult();
|
||||
break;
|
||||
|
||||
case IROp::MultU:
|
||||
// This is an "anti-norm32" case. Let's just zero always.
|
||||
// TODO: If we could know that LO was only needed, we could use MULW and be done.
|
||||
gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2);
|
||||
regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2);
|
||||
makeArgsUnsigned(&lhs, &rhs);
|
||||
MUL(gpr.R(IRREG_LO), lhs, rhs);
|
||||
MUL(regs_.R(IRREG_LO), lhs, rhs);
|
||||
splitMulResult();
|
||||
break;
|
||||
|
||||
case IROp::Madd:
|
||||
gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD);
|
||||
regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD);
|
||||
NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true);
|
||||
MUL(SCRATCH1, lhs, rhs);
|
||||
|
||||
combinePrevMulResult();
|
||||
ADD(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1);
|
||||
ADD(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1);
|
||||
splitMulResult();
|
||||
break;
|
||||
|
||||
case IROp::MaddU:
|
||||
gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD);
|
||||
regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD);
|
||||
makeArgsUnsigned(&lhs, &rhs);
|
||||
MUL(SCRATCH1, lhs, rhs);
|
||||
|
||||
combinePrevMulResult();
|
||||
ADD(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1);
|
||||
ADD(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1);
|
||||
splitMulResult();
|
||||
break;
|
||||
|
||||
case IROp::Msub:
|
||||
gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD);
|
||||
regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD);
|
||||
NormalizeSrc12(inst, &lhs, &rhs, SCRATCH1, SCRATCH2, true);
|
||||
MUL(SCRATCH1, lhs, rhs);
|
||||
|
||||
combinePrevMulResult();
|
||||
SUB(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1);
|
||||
SUB(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1);
|
||||
splitMulResult();
|
||||
break;
|
||||
|
||||
case IROp::MsubU:
|
||||
gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD);
|
||||
regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::ALWAYS_LOAD);
|
||||
makeArgsUnsigned(&lhs, &rhs);
|
||||
MUL(SCRATCH1, lhs, rhs);
|
||||
|
||||
combinePrevMulResult();
|
||||
SUB(gpr.R(IRREG_LO), gpr.R(IRREG_LO), SCRATCH1);
|
||||
SUB(regs_.R(IRREG_LO), regs_.R(IRREG_LO), SCRATCH1);
|
||||
splitMulResult();
|
||||
break;
|
||||
|
||||
@@ -697,18 +697,18 @@ void RiscVJitBackend::CompIR_Div(IRInst inst) {
|
||||
RiscVReg numReg, denomReg;
|
||||
switch (inst.op) {
|
||||
case IROp::Div:
|
||||
gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32);
|
||||
regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32);
|
||||
// We have to do this because of the divide by zero and overflow checks below.
|
||||
NormalizeSrc12(inst, &numReg, &denomReg, SCRATCH1, SCRATCH2, true);
|
||||
DIVW(gpr.R(IRREG_LO), numReg, denomReg);
|
||||
REMW(gpr.R(IRREG_HI), numReg, denomReg);
|
||||
DIVW(regs_.R(IRREG_LO), numReg, denomReg);
|
||||
REMW(regs_.R(IRREG_HI), numReg, denomReg);
|
||||
|
||||
// Now some tweaks for divide by zero and overflow.
|
||||
{
|
||||
// Start with divide by zero, remainder is fine.
|
||||
FixupBranch skipNonZero = BNE(denomReg, R_ZERO);
|
||||
FixupBranch keepNegOne = BGE(numReg, R_ZERO);
|
||||
LI(gpr.R(IRREG_LO), 1);
|
||||
LI(regs_.R(IRREG_LO), 1);
|
||||
SetJumpTarget(keepNegOne);
|
||||
SetJumpTarget(skipNonZero);
|
||||
|
||||
@@ -718,18 +718,18 @@ void RiscVJitBackend::CompIR_Div(IRInst inst) {
|
||||
FixupBranch notMostNegative = BNE(numReg, R_RA);
|
||||
LI(R_RA, -1);
|
||||
FixupBranch notNegativeOne = BNE(denomReg, R_RA);
|
||||
LI(gpr.R(IRREG_HI), -1);
|
||||
LI(regs_.R(IRREG_HI), -1);
|
||||
SetJumpTarget(notNegativeOne);
|
||||
SetJumpTarget(notMostNegative);
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::DivU:
|
||||
gpr.MapDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32);
|
||||
regs_.MapGPRDirtyDirtyInIn(IRREG_LO, IRREG_HI, inst.src1, inst.src2, MapType::AVOID_LOAD_MARK_NORM32);
|
||||
// We have to do this because of the divide by zero check below.
|
||||
NormalizeSrc12(inst, &numReg, &denomReg, SCRATCH1, SCRATCH2, true);
|
||||
DIVUW(gpr.R(IRREG_LO), numReg, denomReg);
|
||||
REMUW(gpr.R(IRREG_HI), numReg, denomReg);
|
||||
DIVUW(regs_.R(IRREG_LO), numReg, denomReg);
|
||||
REMUW(regs_.R(IRREG_HI), numReg, denomReg);
|
||||
|
||||
// On divide by zero, everything is correct already except the 0xFFFF case.
|
||||
{
|
||||
@@ -737,7 +737,7 @@ void RiscVJitBackend::CompIR_Div(IRInst inst) {
|
||||
// Luckily, we don't need SCRATCH2/denomReg anymore.
|
||||
LI(SCRATCH2, 0xFFFF);
|
||||
FixupBranch keepNegOne = BLTU(SCRATCH2, numReg);
|
||||
MV(gpr.R(IRREG_LO), SCRATCH2);
|
||||
MV(regs_.R(IRREG_LO), SCRATCH2);
|
||||
SetJumpTarget(keepNegOne);
|
||||
SetJumpTarget(skipNonZero);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ void RiscVJitBackend::CompIR_Exit(IRInst inst) {
|
||||
break;
|
||||
|
||||
case IROp::ExitToReg:
|
||||
exitReg = gpr.MapGPR(inst.src1);
|
||||
exitReg = regs_.MapGPR(inst.src1);
|
||||
FlushAll();
|
||||
// TODO: If ever we don't read this back in dispatcherPCInSCRATCH1_, we should zero upper.
|
||||
MV(SCRATCH1, exitReg);
|
||||
@@ -72,7 +72,7 @@ void RiscVJitBackend::CompIR_ExitIf(IRInst inst) {
|
||||
switch (inst.op) {
|
||||
case IROp::ExitToConstIfEq:
|
||||
case IROp::ExitToConstIfNeq:
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
// We can't use SCRATCH1, which is destroyed by FlushAll()... but cheat and use R_RA.
|
||||
NormalizeSrc12(inst, &lhs, &rhs, R_RA, SCRATCH2, true);
|
||||
FlushAll();
|
||||
@@ -99,7 +99,7 @@ void RiscVJitBackend::CompIR_ExitIf(IRInst inst) {
|
||||
case IROp::ExitToConstIfGeZ:
|
||||
case IROp::ExitToConstIfLtZ:
|
||||
case IROp::ExitToConstIfLeZ:
|
||||
gpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
NormalizeSrc1(inst, &lhs, SCRATCH2, true);
|
||||
FlushAll();
|
||||
|
||||
|
||||
+136
-137
@@ -39,35 +39,35 @@ void RiscVJitBackend::CompIR_FArith(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::FAdd:
|
||||
fpr.Map(inst);
|
||||
FADD(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
regs_.Map(inst);
|
||||
FADD(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
break;
|
||||
|
||||
case IROp::FSub:
|
||||
fpr.Map(inst);
|
||||
FSUB(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
regs_.Map(inst);
|
||||
FSUB(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
break;
|
||||
|
||||
case IROp::FMul:
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
// We'll assume everyone will make it such that 0 * infinity = NAN properly.
|
||||
// See blame on this comment if that proves untrue.
|
||||
FMUL(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
FMUL(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
break;
|
||||
|
||||
case IROp::FDiv:
|
||||
fpr.Map(inst);
|
||||
FDIV(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
regs_.Map(inst);
|
||||
FDIV(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
break;
|
||||
|
||||
case IROp::FSqrt:
|
||||
fpr.Map(inst);
|
||||
FSQRT(32, fpr.R(inst.dest), fpr.R(inst.src1));
|
||||
regs_.Map(inst);
|
||||
FSQRT(32, regs_.F(inst.dest), regs_.F(inst.src1));
|
||||
break;
|
||||
|
||||
case IROp::FNeg:
|
||||
fpr.Map(inst);
|
||||
FNEG(32, fpr.R(inst.dest), fpr.R(inst.src1));
|
||||
regs_.Map(inst);
|
||||
FNEG(32, regs_.F(inst.dest), regs_.F(inst.src1));
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -83,9 +83,9 @@ void RiscVJitBackend::CompIR_FCondAssign(IRInst inst) {
|
||||
bool maxCondition = inst.op == IROp::FMax;
|
||||
|
||||
// FMin and FMax are used by VFPU and handle NAN/INF as just a larger exponent.
|
||||
fpr.Map(inst);
|
||||
FCLASS(32, SCRATCH1, fpr.R(inst.src1));
|
||||
FCLASS(32, SCRATCH2, fpr.R(inst.src2));
|
||||
regs_.Map(inst);
|
||||
FCLASS(32, SCRATCH1, regs_.F(inst.src1));
|
||||
FCLASS(32, SCRATCH2, regs_.F(inst.src2));
|
||||
|
||||
// If either side is a NAN, it needs to participate in the comparison.
|
||||
OR(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
@@ -94,8 +94,8 @@ void RiscVJitBackend::CompIR_FCondAssign(IRInst inst) {
|
||||
FixupBranch useNormalCond = BEQ(SCRATCH1, R_ZERO);
|
||||
|
||||
// Time to use bits... classify won't help because it ignores -NAN.
|
||||
FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1));
|
||||
FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src2));
|
||||
FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1));
|
||||
FMV(FMv::X, FMv::W, SCRATCH2, regs_.F(inst.src2));
|
||||
|
||||
// If both are negative, we flip the comparison (not two's compliment.)
|
||||
// We cheat and use RA...
|
||||
@@ -116,7 +116,7 @@ void RiscVJitBackend::CompIR_FCondAssign(IRInst inst) {
|
||||
MAX(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
SetJumpTarget(skipSwapCompare);
|
||||
} else {
|
||||
RiscVReg isSrc1LowerReg = gpr.GetAndLockTempR();
|
||||
RiscVReg isSrc1LowerReg = regs_.GetAndLockTempR();
|
||||
SLT(isSrc1LowerReg, SCRATCH1, SCRATCH2);
|
||||
// Flip the flag (to reverse the min/max) based on if both were negative.
|
||||
XOR(isSrc1LowerReg, isSrc1LowerReg, R_RA);
|
||||
@@ -129,14 +129,14 @@ void RiscVJitBackend::CompIR_FCondAssign(IRInst inst) {
|
||||
SetJumpTarget(useSrc1);
|
||||
}
|
||||
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1);
|
||||
FixupBranch finish = J();
|
||||
|
||||
SetJumpTarget(useNormalCond);
|
||||
if (maxCondition)
|
||||
FMAX(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
FMAX(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
else
|
||||
FMIN(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
FMIN(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
SetJumpTarget(finish);
|
||||
}
|
||||
|
||||
@@ -146,21 +146,21 @@ void RiscVJitBackend::CompIR_FAssign(IRInst inst) {
|
||||
switch (inst.op) {
|
||||
case IROp::FMov:
|
||||
if (inst.dest != inst.src1) {
|
||||
fpr.Map(inst);
|
||||
FMV(32, fpr.R(inst.dest), fpr.R(inst.src1));
|
||||
regs_.Map(inst);
|
||||
FMV(32, regs_.F(inst.dest), regs_.F(inst.src1));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::FAbs:
|
||||
fpr.Map(inst);
|
||||
FABS(32, fpr.R(inst.dest), fpr.R(inst.src1));
|
||||
regs_.Map(inst);
|
||||
FABS(32, regs_.F(inst.dest), regs_.F(inst.src1));
|
||||
break;
|
||||
|
||||
case IROp::FSign:
|
||||
{
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
// Check if it's negative zero, either 0x10/0x08 is zero.
|
||||
FCLASS(32, SCRATCH1, fpr.R(inst.src1));
|
||||
FCLASS(32, SCRATCH1, regs_.F(inst.src1));
|
||||
ANDI(SCRATCH1, SCRATCH1, 0x18);
|
||||
SEQZ(SCRATCH1, SCRATCH1);
|
||||
// Okay, it's zero if zero, 1 otherwise. Convert 1 to a constant 1.0.
|
||||
@@ -169,14 +169,14 @@ void RiscVJitBackend::CompIR_FAssign(IRInst inst) {
|
||||
LI(SCRATCH1, 1.0f);
|
||||
|
||||
// Now we just need the sign from it.
|
||||
FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1));
|
||||
FMV(FMv::X, FMv::W, SCRATCH2, regs_.F(inst.src1));
|
||||
// Use a wall to isolate the sign, and combine.
|
||||
SRAIW(SCRATCH2, SCRATCH2, 31);
|
||||
SLLIW(SCRATCH2, SCRATCH2, 31);
|
||||
OR(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
|
||||
SetJumpTarget(skipOne);
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -190,23 +190,23 @@ void RiscVJitBackend::CompIR_FRound(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
// TODO: If this is followed by a GPR transfer, might want to combine.
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::FRound:
|
||||
FCVT(FConv::W, FConv::S, SCRATCH1, fpr.R(inst.src1), Round::NEAREST_EVEN);
|
||||
FCVT(FConv::W, FConv::S, SCRATCH1, regs_.F(inst.src1), Round::NEAREST_EVEN);
|
||||
break;
|
||||
|
||||
case IROp::FTrunc:
|
||||
FCVT(FConv::W, FConv::S, SCRATCH1, fpr.R(inst.src1), Round::TOZERO);
|
||||
FCVT(FConv::W, FConv::S, SCRATCH1, regs_.F(inst.src1), Round::TOZERO);
|
||||
break;
|
||||
|
||||
case IROp::FCeil:
|
||||
FCVT(FConv::W, FConv::S, SCRATCH1, fpr.R(inst.src1), Round::UP);
|
||||
FCVT(FConv::W, FConv::S, SCRATCH1, regs_.F(inst.src1), Round::UP);
|
||||
break;
|
||||
|
||||
case IROp::FFloor:
|
||||
FCVT(FConv::W, FConv::S, SCRATCH1, fpr.R(inst.src1), Round::DOWN);
|
||||
FCVT(FConv::W, FConv::S, SCRATCH1, regs_.F(inst.src1), Round::DOWN);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -214,7 +214,7 @@ void RiscVJitBackend::CompIR_FRound(IRInst inst) {
|
||||
break;
|
||||
}
|
||||
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1);
|
||||
}
|
||||
|
||||
void RiscVJitBackend::CompIR_FCvt(IRInst inst) {
|
||||
@@ -228,9 +228,9 @@ void RiscVJitBackend::CompIR_FCvt(IRInst inst) {
|
||||
|
||||
case IROp::FCvtSW:
|
||||
// TODO: This is probably proceeded by a GPR transfer, might be ideal to combine.
|
||||
fpr.Map(inst);
|
||||
FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1));
|
||||
FCVT(FConv::S, FConv::W, fpr.R(inst.dest), SCRATCH1);
|
||||
regs_.Map(inst);
|
||||
FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1));
|
||||
FCVT(FConv::S, FConv::W, regs_.F(inst.dest), SCRATCH1);
|
||||
break;
|
||||
|
||||
case IROp::FCvtScaledWS:
|
||||
@@ -243,17 +243,17 @@ void RiscVJitBackend::CompIR_FCvt(IRInst inst) {
|
||||
case 3: rm = Round::DOWN; break;
|
||||
}
|
||||
|
||||
tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1);
|
||||
tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1);
|
||||
// Prepare the double src1 and the multiplier.
|
||||
FCVT(FConv::D, FConv::S, fpr.R(inst.dest), fpr.R(inst.src1));
|
||||
FCVT(FConv::D, FConv::S, regs_.F(inst.dest), regs_.F(inst.src1));
|
||||
LI(SCRATCH1, 1UL << (inst.src2 & 0x1F));
|
||||
FCVT(FConv::D, FConv::WU, tempReg, SCRATCH1, rm);
|
||||
|
||||
FMUL(64, fpr.R(inst.dest), fpr.R(inst.dest), tempReg, rm);
|
||||
FMUL(64, regs_.F(inst.dest), regs_.F(inst.dest), tempReg, rm);
|
||||
// NAN and clamping should all be correct.
|
||||
FCVT(FConv::W, FConv::D, SCRATCH1, fpr.R(inst.dest), rm);
|
||||
FCVT(FConv::W, FConv::D, SCRATCH1, regs_.F(inst.dest), rm);
|
||||
// TODO: Could combine with a transfer, often is one...
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1);
|
||||
} else {
|
||||
CompIR_Generic(inst);
|
||||
}
|
||||
@@ -261,14 +261,14 @@ void RiscVJitBackend::CompIR_FCvt(IRInst inst) {
|
||||
|
||||
case IROp::FCvtScaledSW:
|
||||
// TODO: This is probably proceeded by a GPR transfer, might be ideal to combine.
|
||||
tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1);
|
||||
FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1));
|
||||
FCVT(FConv::S, FConv::W, fpr.R(inst.dest), SCRATCH1);
|
||||
tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1);
|
||||
FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1));
|
||||
FCVT(FConv::S, FConv::W, regs_.F(inst.dest), SCRATCH1);
|
||||
|
||||
// Pre-divide so we can avoid any actual divide.
|
||||
LI(SCRATCH1, 1.0f / (1UL << (inst.src2 & 0x1F)));
|
||||
FMV(FMv::W, FMv::X, tempReg, SCRATCH1);
|
||||
FMUL(32, fpr.R(inst.dest), fpr.R(inst.dest), tempReg);
|
||||
FMUL(32, regs_.F(inst.dest), regs_.F(inst.dest), tempReg);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -286,48 +286,48 @@ void RiscVJitBackend::CompIR_FSat(IRInst inst) {
|
||||
FixupBranch skipHigher;
|
||||
switch (inst.op) {
|
||||
case IROp::FSat0_1:
|
||||
tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1);
|
||||
tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1);
|
||||
if (inst.dest != inst.src1)
|
||||
FMV(32, fpr.R(inst.dest), fpr.R(inst.src1));
|
||||
FMV(32, regs_.F(inst.dest), regs_.F(inst.src1));
|
||||
|
||||
// First, set SCRATCH1 = clamp to zero, SCRATCH2 = clamp to one.
|
||||
FCVT(FConv::S, FConv::W, tempReg, R_ZERO);
|
||||
// FLE here is intentional to convert -0.0 to +0.0.
|
||||
FLE(32, SCRATCH1, fpr.R(inst.src1), tempReg);
|
||||
FLE(32, SCRATCH1, regs_.F(inst.src1), tempReg);
|
||||
LI(SCRATCH2, 1.0f);
|
||||
FMV(FMv::W, FMv::X, tempReg, SCRATCH2);
|
||||
FLT(32, SCRATCH2, tempReg, fpr.R(inst.src1));
|
||||
FLT(32, SCRATCH2, tempReg, regs_.F(inst.src1));
|
||||
|
||||
skipLower = BEQ(SCRATCH1, R_ZERO);
|
||||
FCVT(FConv::S, FConv::W, fpr.R(inst.dest), R_ZERO);
|
||||
FCVT(FConv::S, FConv::W, regs_.F(inst.dest), R_ZERO);
|
||||
finishLower = J();
|
||||
|
||||
SetJumpTarget(skipLower);
|
||||
skipHigher = BEQ(SCRATCH2, R_ZERO);
|
||||
// Still has 1.0 in it.
|
||||
FMV(32, fpr.R(inst.dest), tempReg);
|
||||
FMV(32, regs_.F(inst.dest), tempReg);
|
||||
|
||||
SetJumpTarget(finishLower);
|
||||
SetJumpTarget(skipHigher);
|
||||
break;
|
||||
|
||||
case IROp::FSatMinus1_1:
|
||||
tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1);
|
||||
tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1);
|
||||
if (inst.dest != inst.src1)
|
||||
FMV(32, fpr.R(inst.dest), fpr.R(inst.src1));
|
||||
FMV(32, regs_.F(inst.dest), regs_.F(inst.src1));
|
||||
|
||||
// First, set SCRATCH1 = clamp to negative, SCRATCH2 = clamp to positive.
|
||||
LI(SCRATCH2, -1.0f);
|
||||
FMV(FMv::W, FMv::X, tempReg, SCRATCH2);
|
||||
FLT(32, SCRATCH1, fpr.R(inst.src1), tempReg);
|
||||
FLT(32, SCRATCH1, regs_.F(inst.src1), tempReg);
|
||||
FNEG(32, tempReg, tempReg);
|
||||
FLT(32, SCRATCH2, tempReg, fpr.R(inst.src1));
|
||||
FLT(32, SCRATCH2, tempReg, regs_.F(inst.src1));
|
||||
|
||||
// But we can actually do one branch, using sign-injection to keep the original sign.
|
||||
OR(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
|
||||
skipLower = BEQ(SCRATCH1, R_ZERO);
|
||||
FSGNJ(32, fpr.R(inst.dest), tempReg, fpr.R(inst.dest));
|
||||
FSGNJ(32, regs_.F(inst.dest), tempReg, regs_.F(inst.dest));
|
||||
SetJumpTarget(skipLower);
|
||||
break;
|
||||
|
||||
@@ -346,134 +346,134 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) {
|
||||
case IROp::FCmp:
|
||||
switch (inst.dest) {
|
||||
case IRFpCompareMode::False:
|
||||
gpr.SetGPRImm(IRREG_FPCOND, 0);
|
||||
regs_.SetGPRImm(IRREG_FPCOND, 0);
|
||||
break;
|
||||
|
||||
case IRFpCompareMode::EitherUnordered:
|
||||
fpr.Map(inst);
|
||||
gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
FCLASS(32, SCRATCH1, fpr.R(inst.src1));
|
||||
FCLASS(32, SCRATCH2, fpr.R(inst.src2));
|
||||
regs_.Map(inst);
|
||||
regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
FCLASS(32, SCRATCH1, regs_.F(inst.src1));
|
||||
FCLASS(32, SCRATCH2, regs_.F(inst.src2));
|
||||
OR(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
// NAN is 0x100 or 0x200.
|
||||
ANDI(SCRATCH1, SCRATCH1, 0x300);
|
||||
SNEZ(gpr.R(IRREG_FPCOND), SCRATCH1);
|
||||
SNEZ(regs_.R(IRREG_FPCOND), SCRATCH1);
|
||||
break;
|
||||
|
||||
case IRFpCompareMode::EqualOrdered:
|
||||
fpr.Map(inst);
|
||||
gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
FEQ(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
regs_.Map(inst);
|
||||
regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
FEQ(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
break;
|
||||
|
||||
case IRFpCompareMode::EqualUnordered:
|
||||
fpr.Map(inst);
|
||||
gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
FEQ(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
regs_.Map(inst);
|
||||
regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
FEQ(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
|
||||
// Now let's just OR in the unordered check.
|
||||
FCLASS(32, SCRATCH1, fpr.R(inst.src1));
|
||||
FCLASS(32, SCRATCH2, fpr.R(inst.src2));
|
||||
FCLASS(32, SCRATCH1, regs_.F(inst.src1));
|
||||
FCLASS(32, SCRATCH2, regs_.F(inst.src2));
|
||||
OR(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
// NAN is 0x100 or 0x200.
|
||||
ANDI(SCRATCH1, SCRATCH1, 0x300);
|
||||
SNEZ(SCRATCH1, SCRATCH1);
|
||||
OR(gpr.R(IRREG_FPCOND), gpr.R(IRREG_FPCOND), SCRATCH1);
|
||||
OR(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND), SCRATCH1);
|
||||
break;
|
||||
|
||||
case IRFpCompareMode::LessEqualOrdered:
|
||||
fpr.Map(inst);
|
||||
gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
FLE(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
regs_.Map(inst);
|
||||
regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
FLE(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
break;
|
||||
|
||||
case IRFpCompareMode::LessEqualUnordered:
|
||||
fpr.Map(inst);
|
||||
gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
FLT(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src2), fpr.R(inst.src1));
|
||||
SEQZ(gpr.R(IRREG_FPCOND), gpr.R(IRREG_FPCOND));
|
||||
regs_.Map(inst);
|
||||
regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
FLT(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src2), regs_.F(inst.src1));
|
||||
SEQZ(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND));
|
||||
break;
|
||||
|
||||
case IRFpCompareMode::LessOrdered:
|
||||
fpr.Map(inst);
|
||||
gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
FLT(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
regs_.Map(inst);
|
||||
regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
FLT(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
break;
|
||||
|
||||
case IRFpCompareMode::LessUnordered:
|
||||
fpr.Map(inst);
|
||||
gpr.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
FLE(32, gpr.R(IRREG_FPCOND), fpr.R(inst.src2), fpr.R(inst.src1));
|
||||
SEQZ(gpr.R(IRREG_FPCOND), gpr.R(IRREG_FPCOND));
|
||||
regs_.Map(inst);
|
||||
regs_.MapGPR(IRREG_FPCOND, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
FLE(32, regs_.R(IRREG_FPCOND), regs_.F(inst.src2), regs_.F(inst.src1));
|
||||
SEQZ(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::FCmovVfpuCC:
|
||||
gpr.MapGPR(IRREG_VFPU_CC);
|
||||
fpr.Map(inst);
|
||||
regs_.MapGPR(IRREG_VFPU_CC);
|
||||
regs_.Map(inst);
|
||||
if ((inst.src2 & 0xF) == 0) {
|
||||
ANDI(SCRATCH1, gpr.R(IRREG_VFPU_CC), 1);
|
||||
ANDI(SCRATCH1, regs_.R(IRREG_VFPU_CC), 1);
|
||||
} else if (cpu_info.RiscV_Zbs) {
|
||||
BEXTI(SCRATCH1, gpr.R(IRREG_VFPU_CC), inst.src2 & 0xF);
|
||||
BEXTI(SCRATCH1, regs_.R(IRREG_VFPU_CC), inst.src2 & 0xF);
|
||||
} else {
|
||||
SRLI(SCRATCH1, gpr.R(IRREG_VFPU_CC), inst.src2 & 0xF);
|
||||
SRLI(SCRATCH1, regs_.R(IRREG_VFPU_CC), inst.src2 & 0xF);
|
||||
ANDI(SCRATCH1, SCRATCH1, 1);
|
||||
}
|
||||
if ((inst.src2 >> 7) & 1) {
|
||||
FixupBranch skip = BEQ(SCRATCH1, R_ZERO);
|
||||
FMV(32, fpr.R(inst.dest), fpr.R(inst.src1));
|
||||
FMV(32, regs_.F(inst.dest), regs_.F(inst.src1));
|
||||
SetJumpTarget(skip);
|
||||
} else {
|
||||
FixupBranch skip = BNE(SCRATCH1, R_ZERO);
|
||||
FMV(32, fpr.R(inst.dest), fpr.R(inst.src1));
|
||||
FMV(32, regs_.F(inst.dest), regs_.F(inst.src1));
|
||||
SetJumpTarget(skip);
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::FCmpVfpuBit:
|
||||
gpr.MapGPR(IRREG_VFPU_CC, MIPSMap::DIRTY);
|
||||
regs_.MapGPR(IRREG_VFPU_CC, MIPSMap::DIRTY);
|
||||
|
||||
switch (VCondition(inst.dest & 0xF)) {
|
||||
case VC_EQ:
|
||||
fpr.Map(inst);
|
||||
FEQ(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
regs_.Map(inst);
|
||||
FEQ(32, SCRATCH1, regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
break;
|
||||
case VC_NE:
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
// We could almost negate FEQ, except NAN != NAN.
|
||||
// Anything != NAN is false and NAN != NAN is within that, so we only check one side.
|
||||
FCLASS(32, SCRATCH2, fpr.R(inst.src2));
|
||||
FCLASS(32, SCRATCH2, regs_.F(inst.src2));
|
||||
// NAN is 0x100 or 0x200.
|
||||
ANDI(SCRATCH2, SCRATCH2, 0x300);
|
||||
SNEZ(SCRATCH2, SCRATCH2);
|
||||
|
||||
FEQ(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
FEQ(32, SCRATCH1, regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
SEQZ(SCRATCH1, SCRATCH1);
|
||||
// Just OR in whether that side was a NAN so it's always not equal.
|
||||
OR(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
break;
|
||||
case VC_LT:
|
||||
fpr.Map(inst);
|
||||
FLT(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
regs_.Map(inst);
|
||||
FLT(32, SCRATCH1, regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
break;
|
||||
case VC_LE:
|
||||
fpr.Map(inst);
|
||||
FLE(32, SCRATCH1, fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
regs_.Map(inst);
|
||||
FLE(32, SCRATCH1, regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
break;
|
||||
case VC_GT:
|
||||
fpr.Map(inst);
|
||||
FLT(32, SCRATCH1, fpr.R(inst.src2), fpr.R(inst.src1));
|
||||
regs_.Map(inst);
|
||||
FLT(32, SCRATCH1, regs_.F(inst.src2), regs_.F(inst.src1));
|
||||
break;
|
||||
case VC_GE:
|
||||
fpr.Map(inst);
|
||||
FLE(32, SCRATCH1, fpr.R(inst.src2), fpr.R(inst.src1));
|
||||
regs_.Map(inst);
|
||||
FLE(32, SCRATCH1, regs_.F(inst.src2), regs_.F(inst.src1));
|
||||
break;
|
||||
case VC_EZ:
|
||||
case VC_NZ:
|
||||
fpr.MapFPR(inst.src1);
|
||||
regs_.MapFPR(inst.src1);
|
||||
// Zero is either 0x10 or 0x08.
|
||||
FCLASS(32, SCRATCH1, fpr.R(inst.src1));
|
||||
FCLASS(32, SCRATCH1, regs_.F(inst.src1));
|
||||
ANDI(SCRATCH1, SCRATCH1, 0x18);
|
||||
if ((inst.dest & 4) == 0)
|
||||
SNEZ(SCRATCH1, SCRATCH1);
|
||||
@@ -482,9 +482,9 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) {
|
||||
break;
|
||||
case VC_EN:
|
||||
case VC_NN:
|
||||
fpr.MapFPR(inst.src1);
|
||||
regs_.MapFPR(inst.src1);
|
||||
// NAN is either 0x100 or 0x200.
|
||||
FCLASS(32, SCRATCH1, fpr.R(inst.src1));
|
||||
FCLASS(32, SCRATCH1, regs_.F(inst.src1));
|
||||
ANDI(SCRATCH1, SCRATCH1, 0x300);
|
||||
if ((inst.dest & 4) == 0)
|
||||
SNEZ(SCRATCH1, SCRATCH1);
|
||||
@@ -493,9 +493,9 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) {
|
||||
break;
|
||||
case VC_EI:
|
||||
case VC_NI:
|
||||
fpr.MapFPR(inst.src1);
|
||||
regs_.MapFPR(inst.src1);
|
||||
// Infinity is either 0x80 or 0x01.
|
||||
FCLASS(32, SCRATCH1, fpr.R(inst.src1));
|
||||
FCLASS(32, SCRATCH1, regs_.F(inst.src1));
|
||||
ANDI(SCRATCH1, SCRATCH1, 0x81);
|
||||
if ((inst.dest & 4) == 0)
|
||||
SNEZ(SCRATCH1, SCRATCH1);
|
||||
@@ -504,9 +504,9 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) {
|
||||
break;
|
||||
case VC_ES:
|
||||
case VC_NS:
|
||||
fpr.MapFPR(inst.src1);
|
||||
regs_.MapFPR(inst.src1);
|
||||
// Infinity is either 0x80 or 0x01, NAN is either 0x100 or 0x200.
|
||||
FCLASS(32, SCRATCH1, fpr.R(inst.src1));
|
||||
FCLASS(32, SCRATCH1, regs_.F(inst.src1));
|
||||
ANDI(SCRATCH1, SCRATCH1, 0x381);
|
||||
if ((inst.dest & 4) == 0)
|
||||
SNEZ(SCRATCH1, SCRATCH1);
|
||||
@@ -521,15 +521,15 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) {
|
||||
break;
|
||||
}
|
||||
|
||||
ANDI(gpr.R(IRREG_VFPU_CC), gpr.R(IRREG_VFPU_CC), ~(1 << (inst.dest >> 4)));
|
||||
ANDI(regs_.R(IRREG_VFPU_CC), regs_.R(IRREG_VFPU_CC), ~(1 << (inst.dest >> 4)));
|
||||
if ((inst.dest >> 4) != 0)
|
||||
SLLI(SCRATCH1, SCRATCH1, inst.dest >> 4);
|
||||
OR(gpr.R(IRREG_VFPU_CC), gpr.R(IRREG_VFPU_CC), SCRATCH1);
|
||||
OR(regs_.R(IRREG_VFPU_CC), regs_.R(IRREG_VFPU_CC), SCRATCH1);
|
||||
break;
|
||||
|
||||
case IROp::FCmpVfpuAggregate:
|
||||
gpr.MapGPR(IRREG_VFPU_CC, MIPSMap::DIRTY);
|
||||
ANDI(SCRATCH1, gpr.R(IRREG_VFPU_CC), inst.dest);
|
||||
regs_.MapGPR(IRREG_VFPU_CC, MIPSMap::DIRTY);
|
||||
ANDI(SCRATCH1, regs_.R(IRREG_VFPU_CC), inst.dest);
|
||||
// This is the "any bit", easy.
|
||||
SNEZ(SCRATCH2, SCRATCH1);
|
||||
// To compare to inst.dest for "all", let's simply subtract it and compare to zero.
|
||||
@@ -541,8 +541,8 @@ void RiscVJitBackend::CompIR_FCompare(IRInst inst) {
|
||||
OR(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
|
||||
// Reject those any/all bits and replace them with our own.
|
||||
ANDI(gpr.R(IRREG_VFPU_CC), gpr.R(IRREG_VFPU_CC), ~0x30);
|
||||
OR(gpr.R(IRREG_VFPU_CC), gpr.R(IRREG_VFPU_CC), SCRATCH1);
|
||||
ANDI(regs_.R(IRREG_VFPU_CC), regs_.R(IRREG_VFPU_CC), ~0x30);
|
||||
OR(regs_.R(IRREG_VFPU_CC), regs_.R(IRREG_VFPU_CC), SCRATCH1);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -581,21 +581,20 @@ void RiscVJitBackend::CompIR_FSpecial(IRInst inst) {
|
||||
#endif
|
||||
|
||||
auto callFuncF_F = [&](float (*func)(float)){
|
||||
gpr.FlushBeforeCall();
|
||||
fpr.FlushBeforeCall();
|
||||
regs_.FlushBeforeCall();
|
||||
// It might be in a non-volatile register.
|
||||
if (fpr.IsFPRMapped(inst.src1)) {
|
||||
FMV(32, F10, fpr.R(inst.src1));
|
||||
if (regs_.IsFPRMapped(inst.src1)) {
|
||||
FMV(32, F10, regs_.F(inst.src1));
|
||||
} else {
|
||||
int offset = offsetof(MIPSState, f) + inst.src1 * 4;
|
||||
FL(32, F10, CTXREG, offset);
|
||||
}
|
||||
QuickCallFunction(func, SCRATCH1);
|
||||
|
||||
fpr.MapFPR(inst.dest, MIPSMap::NOINIT);
|
||||
regs_.MapFPR(inst.dest, MIPSMap::NOINIT);
|
||||
// If it's already F10, we're done - MapReg doesn't actually overwrite the reg in that case.
|
||||
if (fpr.R(inst.dest) != F10) {
|
||||
FMV(32, fpr.R(inst.dest), F10);
|
||||
if (regs_.F(inst.dest) != F10) {
|
||||
FMV(32, regs_.F(inst.dest), F10);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -610,27 +609,27 @@ void RiscVJitBackend::CompIR_FSpecial(IRInst inst) {
|
||||
break;
|
||||
|
||||
case IROp::FRSqrt:
|
||||
tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1);
|
||||
FSQRT(32, fpr.R(inst.dest), fpr.R(inst.src1));
|
||||
tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1);
|
||||
FSQRT(32, regs_.F(inst.dest), regs_.F(inst.src1));
|
||||
|
||||
// Ugh, we can't really avoid a temp here. Probably not worth a permanent one.
|
||||
LI(SCRATCH1, 1.0f);
|
||||
FMV(FMv::W, FMv::X, tempReg, SCRATCH1);
|
||||
FDIV(32, fpr.R(inst.dest), tempReg, fpr.R(inst.dest));
|
||||
FDIV(32, regs_.F(inst.dest), tempReg, regs_.F(inst.dest));
|
||||
break;
|
||||
|
||||
case IROp::FRecip:
|
||||
if (inst.dest != inst.src1) {
|
||||
// This is the easy case.
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
LI(SCRATCH1, 1.0f);
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1);
|
||||
FDIV(32, fpr.R(inst.dest), fpr.R(inst.dest), fpr.R(inst.src1));
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1);
|
||||
FDIV(32, regs_.F(inst.dest), regs_.F(inst.dest), regs_.F(inst.src1));
|
||||
} else {
|
||||
tempReg = fpr.MapDirtyInTemp(inst.dest, inst.src1);
|
||||
tempReg = regs_.MapFPRDirtyInTemp(inst.dest, inst.src1);
|
||||
LI(SCRATCH1, 1.0f);
|
||||
FMV(FMv::W, FMv::X, tempReg, SCRATCH1);
|
||||
FDIV(32, fpr.R(inst.dest), tempReg, fpr.R(inst.src1));
|
||||
FDIV(32, regs_.F(inst.dest), tempReg, regs_.F(inst.src1));
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -35,18 +35,18 @@ using namespace RiscVGen;
|
||||
using namespace RiscVJitConstants;
|
||||
|
||||
void RiscVJitBackend::SetScratch1ToSrc1Address(IRReg src1) {
|
||||
gpr.MapGPR(src1);
|
||||
regs_.MapGPR(src1);
|
||||
#ifdef MASKED_PSP_MEMORY
|
||||
SLLIW(SCRATCH1, gpr.R(src1), 2);
|
||||
SLLIW(SCRATCH1, regs_.R(src1), 2);
|
||||
SRLIW(SCRATCH1, SCRATCH1, 2);
|
||||
ADD(SCRATCH1, SCRATCH1, MEMBASEREG);
|
||||
#else
|
||||
// Clear the top bits to be safe.
|
||||
if (cpu_info.RiscV_Zba) {
|
||||
ADD_UW(SCRATCH1, gpr.R(src1), MEMBASEREG);
|
||||
ADD_UW(SCRATCH1, regs_.R(src1), MEMBASEREG);
|
||||
} else {
|
||||
_assert_(XLEN == 64);
|
||||
SLLI(SCRATCH1, gpr.R(src1), 32);
|
||||
SLLI(SCRATCH1, regs_.R(src1), 32);
|
||||
SRLI(SCRATCH1, SCRATCH1, 32);
|
||||
ADD(SCRATCH1, SCRATCH1, MEMBASEREG);
|
||||
}
|
||||
@@ -66,7 +66,7 @@ int32_t RiscVJitBackend::AdjustForAddressOffset(RiscVGen::RiscVReg *reg, int32_t
|
||||
void RiscVJitBackend::CompIR_Load(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
gpr.SpillLockGPR(inst.dest, inst.src1);
|
||||
regs_.SpillLockGPR(inst.dest, inst.src1);
|
||||
RiscVReg addrReg = INVALID_REG;
|
||||
if (inst.src1 == MIPS_REG_ZERO) {
|
||||
// This will get changed by AdjustForAddressOffset.
|
||||
@@ -74,14 +74,14 @@ void RiscVJitBackend::CompIR_Load(IRInst inst) {
|
||||
#ifdef MASKED_PSP_MEMORY
|
||||
inst.constant &= Memory::MEMVIEW32_MASK;
|
||||
#endif
|
||||
} else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) {
|
||||
addrReg = gpr.MapGPRAsPointer(inst.src1);
|
||||
} else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) {
|
||||
addrReg = regs_.MapGPRAsPointer(inst.src1);
|
||||
} else {
|
||||
SetScratch1ToSrc1Address(inst.src1);
|
||||
addrReg = SCRATCH1;
|
||||
}
|
||||
// With NOINIT, MapReg won't subtract MEMBASEREG even if dest == src1.
|
||||
gpr.MapGPR(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
regs_.MapGPR(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
|
||||
s32 imm = AdjustForAddressOffset(&addrReg, inst.constant);
|
||||
|
||||
@@ -89,29 +89,29 @@ void RiscVJitBackend::CompIR_Load(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Load8:
|
||||
LBU(gpr.R(inst.dest), addrReg, imm);
|
||||
LBU(regs_.R(inst.dest), addrReg, imm);
|
||||
break;
|
||||
|
||||
case IROp::Load8Ext:
|
||||
LB(gpr.R(inst.dest), addrReg, imm);
|
||||
LB(regs_.R(inst.dest), addrReg, imm);
|
||||
break;
|
||||
|
||||
case IROp::Load16:
|
||||
LHU(gpr.R(inst.dest), addrReg, imm);
|
||||
LHU(regs_.R(inst.dest), addrReg, imm);
|
||||
break;
|
||||
|
||||
case IROp::Load16Ext:
|
||||
LH(gpr.R(inst.dest), addrReg, imm);
|
||||
LH(regs_.R(inst.dest), addrReg, imm);
|
||||
break;
|
||||
|
||||
case IROp::Load32:
|
||||
LW(gpr.R(inst.dest), addrReg, imm);
|
||||
LW(regs_.R(inst.dest), addrReg, imm);
|
||||
break;
|
||||
|
||||
case IROp::Load32Linked:
|
||||
if (inst.dest != MIPS_REG_ZERO)
|
||||
LW(gpr.R(inst.dest), addrReg, imm);
|
||||
gpr.SetGPRImm(IRREG_LLBIT, 1);
|
||||
LW(regs_.R(inst.dest), addrReg, imm);
|
||||
regs_.SetGPRImm(IRREG_LLBIT, 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -146,8 +146,8 @@ void RiscVJitBackend::CompIR_FLoad(IRInst inst) {
|
||||
#ifdef MASKED_PSP_MEMORY
|
||||
inst.constant &= Memory::MEMVIEW32_MASK;
|
||||
#endif
|
||||
} else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) {
|
||||
addrReg = gpr.MapGPRAsPointer(inst.src1);
|
||||
} else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) {
|
||||
addrReg = regs_.MapGPRAsPointer(inst.src1);
|
||||
} else {
|
||||
SetScratch1ToSrc1Address(inst.src1);
|
||||
addrReg = SCRATCH1;
|
||||
@@ -159,8 +159,8 @@ void RiscVJitBackend::CompIR_FLoad(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::LoadFloat:
|
||||
fpr.MapFPR(inst.dest, MIPSMap::NOINIT);
|
||||
FL(32, fpr.R(inst.dest), addrReg, imm);
|
||||
regs_.MapFPR(inst.dest, MIPSMap::NOINIT);
|
||||
FL(32, regs_.F(inst.dest), addrReg, imm);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -179,8 +179,8 @@ void RiscVJitBackend::CompIR_VecLoad(IRInst inst) {
|
||||
#ifdef MASKED_PSP_MEMORY
|
||||
inst.constant &= Memory::MEMVIEW32_MASK;
|
||||
#endif
|
||||
} else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) {
|
||||
addrReg = gpr.MapGPRAsPointer(inst.src1);
|
||||
} else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) {
|
||||
addrReg = regs_.MapGPRAsPointer(inst.src1);
|
||||
} else {
|
||||
SetScratch1ToSrc1Address(inst.src1);
|
||||
addrReg = SCRATCH1;
|
||||
@@ -195,8 +195,8 @@ void RiscVJitBackend::CompIR_VecLoad(IRInst inst) {
|
||||
case IROp::LoadVec4:
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
// Spilling is okay.
|
||||
fpr.MapFPR(inst.dest + i, MIPSMap::NOINIT);
|
||||
FL(32, fpr.R(inst.dest + i), addrReg, imm + 4 * i);
|
||||
regs_.MapFPR(inst.dest + i, MIPSMap::NOINIT);
|
||||
FL(32, regs_.F(inst.dest + i), addrReg, imm + 4 * i);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -209,7 +209,7 @@ void RiscVJitBackend::CompIR_VecLoad(IRInst inst) {
|
||||
void RiscVJitBackend::CompIR_Store(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
gpr.SpillLockGPR(inst.src3, inst.src1);
|
||||
regs_.SpillLockGPR(inst.src3, inst.src1);
|
||||
RiscVReg addrReg = INVALID_REG;
|
||||
if (inst.src1 == MIPS_REG_ZERO) {
|
||||
// This will get changed by AdjustForAddressOffset.
|
||||
@@ -217,15 +217,15 @@ void RiscVJitBackend::CompIR_Store(IRInst inst) {
|
||||
#ifdef MASKED_PSP_MEMORY
|
||||
inst.constant &= Memory::MEMVIEW32_MASK;
|
||||
#endif
|
||||
} else if ((jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) {
|
||||
addrReg = gpr.MapGPRAsPointer(inst.src1);
|
||||
} else if ((jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) {
|
||||
addrReg = regs_.MapGPRAsPointer(inst.src1);
|
||||
} else {
|
||||
SetScratch1ToSrc1Address(inst.src1);
|
||||
addrReg = SCRATCH1;
|
||||
}
|
||||
RiscVReg valueReg = gpr.TryMapTempImm(inst.src3);
|
||||
RiscVReg valueReg = regs_.TryMapTempImm(inst.src3);
|
||||
if (valueReg == INVALID_REG)
|
||||
valueReg = gpr.MapGPR(inst.src3);
|
||||
valueReg = regs_.MapGPR(inst.src3);
|
||||
|
||||
s32 imm = AdjustForAddressOffset(&addrReg, inst.constant);
|
||||
|
||||
@@ -255,7 +255,7 @@ void RiscVJitBackend::CompIR_CondStore(IRInst inst) {
|
||||
if (inst.op != IROp::Store32Conditional)
|
||||
INVALIDOP;
|
||||
|
||||
gpr.SpillLockGPR(IRREG_LLBIT, inst.src3, inst.src1);
|
||||
regs_.SpillLockGPR(IRREG_LLBIT, inst.src3, inst.src1);
|
||||
RiscVReg addrReg = INVALID_REG;
|
||||
if (inst.src1 == MIPS_REG_ZERO) {
|
||||
// This will get changed by AdjustForAddressOffset.
|
||||
@@ -263,28 +263,28 @@ void RiscVJitBackend::CompIR_CondStore(IRInst inst) {
|
||||
#ifdef MASKED_PSP_MEMORY
|
||||
inst.constant &= Memory::MEMVIEW32_MASK;
|
||||
#endif
|
||||
} else if ((jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) {
|
||||
addrReg = gpr.MapGPRAsPointer(inst.src1);
|
||||
} else if ((jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) && inst.src3 != inst.src1) {
|
||||
addrReg = regs_.MapGPRAsPointer(inst.src1);
|
||||
} else {
|
||||
SetScratch1ToSrc1Address(inst.src1);
|
||||
addrReg = SCRATCH1;
|
||||
}
|
||||
gpr.MapGPR(inst.src3, inst.dest == MIPS_REG_ZERO ? MIPSMap::INIT : MIPSMap::DIRTY);
|
||||
gpr.MapGPR(IRREG_LLBIT);
|
||||
regs_.MapGPR(inst.src3, inst.dest == MIPS_REG_ZERO ? MIPSMap::INIT : MIPSMap::DIRTY);
|
||||
regs_.MapGPR(IRREG_LLBIT);
|
||||
|
||||
s32 imm = AdjustForAddressOffset(&addrReg, inst.constant);
|
||||
|
||||
// TODO: Safe memory? Or enough to have crash handler + validate?
|
||||
|
||||
FixupBranch condFailed = BEQ(gpr.R(IRREG_LLBIT), R_ZERO);
|
||||
SW(gpr.R(inst.src3), addrReg, imm);
|
||||
FixupBranch condFailed = BEQ(regs_.R(IRREG_LLBIT), R_ZERO);
|
||||
SW(regs_.R(inst.src3), addrReg, imm);
|
||||
|
||||
if (inst.dest != MIPS_REG_ZERO) {
|
||||
LI(gpr.R(inst.dest), 1);
|
||||
LI(regs_.R(inst.dest), 1);
|
||||
FixupBranch finish = J();
|
||||
|
||||
SetJumpTarget(condFailed);
|
||||
LI(gpr.R(inst.dest), 0);
|
||||
LI(regs_.R(inst.dest), 0);
|
||||
SetJumpTarget(finish);
|
||||
} else {
|
||||
SetJumpTarget(condFailed);
|
||||
@@ -317,8 +317,8 @@ void RiscVJitBackend::CompIR_FStore(IRInst inst) {
|
||||
#ifdef MASKED_PSP_MEMORY
|
||||
inst.constant &= Memory::MEMVIEW32_MASK;
|
||||
#endif
|
||||
} else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) {
|
||||
addrReg = gpr.MapGPRAsPointer(inst.src1);
|
||||
} else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) {
|
||||
addrReg = regs_.MapGPRAsPointer(inst.src1);
|
||||
} else {
|
||||
SetScratch1ToSrc1Address(inst.src1);
|
||||
addrReg = SCRATCH1;
|
||||
@@ -330,8 +330,8 @@ void RiscVJitBackend::CompIR_FStore(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::StoreFloat:
|
||||
fpr.MapFPR(inst.src3);
|
||||
FS(32, fpr.R(inst.src3), addrReg, imm);
|
||||
regs_.MapFPR(inst.src3);
|
||||
FS(32, regs_.F(inst.src3), addrReg, imm);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -350,8 +350,8 @@ void RiscVJitBackend::CompIR_VecStore(IRInst inst) {
|
||||
#ifdef MASKED_PSP_MEMORY
|
||||
inst.constant &= Memory::MEMVIEW32_MASK;
|
||||
#endif
|
||||
} else if (jo.cachePointers || gpr.IsGPRMappedAsPointer(inst.src1)) {
|
||||
addrReg = gpr.MapGPRAsPointer(inst.src1);
|
||||
} else if (jo.cachePointers || regs_.IsGPRMappedAsPointer(inst.src1)) {
|
||||
addrReg = regs_.MapGPRAsPointer(inst.src1);
|
||||
} else {
|
||||
SetScratch1ToSrc1Address(inst.src1);
|
||||
addrReg = SCRATCH1;
|
||||
@@ -366,8 +366,8 @@ void RiscVJitBackend::CompIR_VecStore(IRInst inst) {
|
||||
case IROp::StoreVec4:
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
// Spilling is okay, though not ideal.
|
||||
fpr.MapFPR(inst.src3 + i);
|
||||
FS(32, fpr.R(inst.src3 + i), addrReg, imm + 4 * i);
|
||||
regs_.MapFPR(inst.src3 + i);
|
||||
FS(32, regs_.F(inst.src3 + i), addrReg, imm + 4 * i);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -45,18 +45,18 @@ void RiscVJitBackend::CompIR_Basic(IRInst inst) {
|
||||
case IROp::SetConst:
|
||||
// Sign extend all constants. We get 0xFFFFFFFF sometimes, and it's more work to truncate.
|
||||
// The register only holds 32 bits in the end anyway.
|
||||
gpr.SetGPRImm(inst.dest, (int32_t)inst.constant);
|
||||
regs_.SetGPRImm(inst.dest, (int32_t)inst.constant);
|
||||
break;
|
||||
|
||||
case IROp::SetConstF:
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
if (inst.constant == 0) {
|
||||
FCVT(FConv::S, FConv::W, fpr.R(inst.dest), R_ZERO);
|
||||
FCVT(FConv::S, FConv::W, regs_.F(inst.dest), R_ZERO);
|
||||
} else {
|
||||
// TODO: In the future, could use FLI if it's approved.
|
||||
// Also, is FCVT faster?
|
||||
LI(SCRATCH1, (int32_t)inst.constant);
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -70,8 +70,8 @@ void RiscVJitBackend::CompIR_Basic(IRInst inst) {
|
||||
break;
|
||||
|
||||
case IROp::SetPC:
|
||||
gpr.Map(inst);
|
||||
MovToPC(gpr.R(inst.src1));
|
||||
regs_.Map(inst);
|
||||
MovToPC(regs_.R(inst.src1));
|
||||
break;
|
||||
|
||||
case IROp::SetPCConst:
|
||||
@@ -90,48 +90,48 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::SetCtrlVFPU:
|
||||
gpr.SetGPRImm(IRREG_VFPU_CTRL_BASE + inst.dest, (int32_t)inst.constant);
|
||||
regs_.SetGPRImm(IRREG_VFPU_CTRL_BASE + inst.dest, (int32_t)inst.constant);
|
||||
break;
|
||||
|
||||
case IROp::SetCtrlVFPUReg:
|
||||
gpr.MapDirtyIn(IRREG_VFPU_CTRL_BASE + inst.dest, inst.src1);
|
||||
MV(gpr.R(IRREG_VFPU_CTRL_BASE + inst.dest), gpr.R(inst.src1));
|
||||
gpr.MarkGPRDirty(IRREG_VFPU_CTRL_BASE + inst.dest, gpr.IsNormalized32(inst.src1));
|
||||
regs_.MapGPRDirtyIn(IRREG_VFPU_CTRL_BASE + inst.dest, inst.src1);
|
||||
MV(regs_.R(IRREG_VFPU_CTRL_BASE + inst.dest), regs_.R(inst.src1));
|
||||
regs_.MarkGPRDirty(IRREG_VFPU_CTRL_BASE + inst.dest, regs_.IsNormalized32(inst.src1));
|
||||
break;
|
||||
|
||||
case IROp::SetCtrlVFPUFReg:
|
||||
gpr.MapGPR(IRREG_VFPU_CTRL_BASE + inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
fpr.MapFPR(inst.src1);
|
||||
FMV(FMv::X, FMv::W, gpr.R(IRREG_VFPU_CTRL_BASE + inst.dest), fpr.R(inst.src1));
|
||||
regs_.MapGPR(IRREG_VFPU_CTRL_BASE + inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
regs_.MapFPR(inst.src1);
|
||||
FMV(FMv::X, FMv::W, regs_.R(IRREG_VFPU_CTRL_BASE + inst.dest), regs_.F(inst.src1));
|
||||
break;
|
||||
|
||||
case IROp::FpCondFromReg:
|
||||
gpr.MapDirtyIn(IRREG_FPCOND, inst.src1);
|
||||
MV(gpr.R(IRREG_FPCOND), gpr.R(inst.src1));
|
||||
regs_.MapGPRDirtyIn(IRREG_FPCOND, inst.src1);
|
||||
MV(regs_.R(IRREG_FPCOND), regs_.R(inst.src1));
|
||||
break;
|
||||
|
||||
case IROp::FpCondToReg:
|
||||
gpr.MapDirtyIn(inst.dest, IRREG_FPCOND);
|
||||
MV(gpr.R(inst.dest), gpr.R(IRREG_FPCOND));
|
||||
gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(IRREG_FPCOND));
|
||||
regs_.MapGPRDirtyIn(inst.dest, IRREG_FPCOND);
|
||||
MV(regs_.R(inst.dest), regs_.R(IRREG_FPCOND));
|
||||
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_FPCOND));
|
||||
break;
|
||||
|
||||
case IROp::FpCtrlFromReg:
|
||||
gpr.MapDirtyIn(IRREG_FPCOND, inst.src1, MapType::AVOID_LOAD_MARK_NORM32);
|
||||
regs_.MapGPRDirtyIn(IRREG_FPCOND, inst.src1, MapType::AVOID_LOAD_MARK_NORM32);
|
||||
LI(SCRATCH1, 0x0181FFFF);
|
||||
AND(SCRATCH1, gpr.R(inst.src1), SCRATCH1);
|
||||
AND(SCRATCH1, regs_.R(inst.src1), SCRATCH1);
|
||||
// Extract the new fpcond value.
|
||||
if (cpu_info.RiscV_Zbs) {
|
||||
BEXTI(gpr.R(IRREG_FPCOND), SCRATCH1, 23);
|
||||
BEXTI(regs_.R(IRREG_FPCOND), SCRATCH1, 23);
|
||||
} else {
|
||||
SRLI(gpr.R(IRREG_FPCOND), SCRATCH1, 23);
|
||||
ANDI(gpr.R(IRREG_FPCOND), gpr.R(IRREG_FPCOND), 1);
|
||||
SRLI(regs_.R(IRREG_FPCOND), SCRATCH1, 23);
|
||||
ANDI(regs_.R(IRREG_FPCOND), regs_.R(IRREG_FPCOND), 1);
|
||||
}
|
||||
SW(SCRATCH1, CTXREG, IRREG_FCR31 * 4);
|
||||
break;
|
||||
|
||||
case IROp::FpCtrlToReg:
|
||||
gpr.MapDirtyIn(inst.dest, IRREG_FPCOND, MapType::AVOID_LOAD_MARK_NORM32);
|
||||
regs_.MapGPRDirtyIn(inst.dest, IRREG_FPCOND, MapType::AVOID_LOAD_MARK_NORM32);
|
||||
// Load fcr31 and clear the fpcond bit.
|
||||
LW(SCRATCH1, CTXREG, IRREG_FCR31 * 4);
|
||||
if (cpu_info.RiscV_Zbs) {
|
||||
@@ -142,34 +142,34 @@ void RiscVJitBackend::CompIR_Transfer(IRInst inst) {
|
||||
}
|
||||
|
||||
// Now get the correct fpcond bit.
|
||||
ANDI(SCRATCH2, gpr.R(IRREG_FPCOND), 1);
|
||||
ANDI(SCRATCH2, regs_.R(IRREG_FPCOND), 1);
|
||||
SLLI(SCRATCH2, SCRATCH2, 23);
|
||||
OR(gpr.R(inst.dest), SCRATCH1, SCRATCH2);
|
||||
OR(regs_.R(inst.dest), SCRATCH1, SCRATCH2);
|
||||
|
||||
// Also update mips->fcr31 while we're here.
|
||||
SW(gpr.R(inst.dest), CTXREG, IRREG_FCR31 * 4);
|
||||
SW(regs_.R(inst.dest), CTXREG, IRREG_FCR31 * 4);
|
||||
break;
|
||||
|
||||
case IROp::VfpuCtrlToReg:
|
||||
gpr.MapDirtyIn(inst.dest, IRREG_VFPU_CTRL_BASE + inst.src1);
|
||||
MV(gpr.R(inst.dest), gpr.R(IRREG_VFPU_CTRL_BASE + inst.src1));
|
||||
gpr.MarkGPRDirty(inst.dest, gpr.IsNormalized32(IRREG_VFPU_CTRL_BASE + inst.src1));
|
||||
regs_.MapGPRDirtyIn(inst.dest, IRREG_VFPU_CTRL_BASE + inst.src1);
|
||||
MV(regs_.R(inst.dest), regs_.R(IRREG_VFPU_CTRL_BASE + inst.src1));
|
||||
regs_.MarkGPRDirty(inst.dest, regs_.IsNormalized32(IRREG_VFPU_CTRL_BASE + inst.src1));
|
||||
break;
|
||||
|
||||
case IROp::FMovFromGPR:
|
||||
fpr.MapFPR(inst.dest, MIPSMap::NOINIT);
|
||||
if (gpr.IsGPRImm(inst.src1) && gpr.GetGPRImm(inst.src1) == 0) {
|
||||
FCVT(FConv::S, FConv::W, fpr.R(inst.dest), R_ZERO);
|
||||
regs_.MapFPR(inst.dest, MIPSMap::NOINIT);
|
||||
if (regs_.IsGPRImm(inst.src1) && regs_.GetGPRImm(inst.src1) == 0) {
|
||||
FCVT(FConv::S, FConv::W, regs_.F(inst.dest), R_ZERO);
|
||||
} else {
|
||||
gpr.MapGPR(inst.src1);
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest), gpr.R(inst.src1));
|
||||
regs_.MapGPR(inst.src1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest), regs_.R(inst.src1));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::FMovToGPR:
|
||||
gpr.MapGPR(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
fpr.MapFPR(inst.src1);
|
||||
FMV(FMv::X, FMv::W, gpr.R(inst.dest), fpr.R(inst.src1));
|
||||
regs_.MapGPR(inst.dest, MIPSMap::NOINIT | MIPSMap::MARK_NORM32);
|
||||
regs_.MapFPR(inst.src1);
|
||||
FMV(FMv::X, FMv::W, regs_.R(inst.dest), regs_.F(inst.src1));
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -40,36 +40,36 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Vec4Init:
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
|
||||
// TODO: Check if FCVT/FMV/FL is better.
|
||||
switch ((Vec4Init)inst.src1) {
|
||||
case Vec4Init::AllZERO:
|
||||
for (int i = 0; i < 4; ++i)
|
||||
FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO);
|
||||
FCVT(FConv::S, FConv::W, regs_.F(inst.dest + i), R_ZERO);
|
||||
break;
|
||||
|
||||
case Vec4Init::AllONE:
|
||||
LI(SCRATCH1, 1.0f);
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1);
|
||||
for (int i = 1; i < 4; ++i)
|
||||
FMV(32, fpr.R(inst.dest + i), fpr.R(inst.dest));
|
||||
FMV(32, regs_.F(inst.dest + i), regs_.F(inst.dest));
|
||||
break;
|
||||
|
||||
case Vec4Init::AllMinusONE:
|
||||
LI(SCRATCH1, -1.0f);
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1);
|
||||
for (int i = 1; i < 4; ++i)
|
||||
FMV(32, fpr.R(inst.dest + i), fpr.R(inst.dest));
|
||||
FMV(32, regs_.F(inst.dest + i), regs_.F(inst.dest));
|
||||
break;
|
||||
|
||||
case Vec4Init::Set_1000:
|
||||
LI(SCRATCH1, 1.0f);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (i == 0)
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest + i), SCRATCH1);
|
||||
else
|
||||
FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO);
|
||||
FCVT(FConv::S, FConv::W, regs_.F(inst.dest + i), R_ZERO);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -77,9 +77,9 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) {
|
||||
LI(SCRATCH1, 1.0f);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (i == 1)
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest + i), SCRATCH1);
|
||||
else
|
||||
FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO);
|
||||
FCVT(FConv::S, FConv::W, regs_.F(inst.dest + i), R_ZERO);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -87,9 +87,9 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) {
|
||||
LI(SCRATCH1, 1.0f);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (i == 2)
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest + i), SCRATCH1);
|
||||
else
|
||||
FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO);
|
||||
FCVT(FConv::S, FConv::W, regs_.F(inst.dest + i), R_ZERO);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -97,9 +97,9 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) {
|
||||
LI(SCRATCH1, 1.0f);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (i == 3)
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest + i), SCRATCH1);
|
||||
else
|
||||
FCVT(FConv::S, FConv::W, fpr.R(inst.dest + i), R_ZERO);
|
||||
FCVT(FConv::S, FConv::W, regs_.F(inst.dest + i), R_ZERO);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -107,7 +107,7 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) {
|
||||
|
||||
case IROp::Vec4Shuffle:
|
||||
if (inst.dest == inst.src1) {
|
||||
RiscVReg tempReg = fpr.Map4DirtyInTemp(inst.dest, inst.src1);
|
||||
RiscVReg tempReg = regs_.MapFPR4DirtyInTemp(inst.dest, inst.src1);
|
||||
|
||||
// Try to find the least swaps needed to move in place, never worse than 6 FMVs.
|
||||
// Would be better with a vmerge and vector regs.
|
||||
@@ -121,13 +121,13 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) {
|
||||
auto moveChained = [&](const std::vector<int> &lanes, bool rotate) {
|
||||
int firstState = state[lanes.front()];
|
||||
if (rotate)
|
||||
FMV(32, tempReg, fpr.R(inst.dest + lanes.front()));
|
||||
FMV(32, tempReg, regs_.F(inst.dest + lanes.front()));
|
||||
for (size_t i = 1; i < lanes.size(); ++i) {
|
||||
FMV(32, fpr.R(inst.dest + lanes[i - 1]), fpr.R(inst.dest + lanes[i]));
|
||||
FMV(32, regs_.F(inst.dest + lanes[i - 1]), regs_.F(inst.dest + lanes[i]));
|
||||
state[lanes[i - 1]] = state[lanes[i]];
|
||||
}
|
||||
if (rotate) {
|
||||
FMV(32, fpr.R(inst.dest + lanes.back()), tempReg);
|
||||
FMV(32, regs_.F(inst.dest + lanes.back()), tempReg);
|
||||
state[lanes.back()] = firstState;
|
||||
}
|
||||
};
|
||||
@@ -158,26 +158,26 @@ void RiscVJitBackend::CompIR_VecAssign(IRInst inst) {
|
||||
moveChained({ neededByDepth2, neededBy, i, foundIn }, neededByDepth3 == foundIn);
|
||||
}
|
||||
} else {
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int lane = (inst.src2 >> (i * 2)) & 3;
|
||||
FMV(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + lane));
|
||||
FMV(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + lane));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Vec4Blend:
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int which = (inst.constant >> i) & 1;
|
||||
FMV(32, fpr.R(inst.dest + i), fpr.R((which ? inst.src2 : inst.src1) + i));
|
||||
FMV(32, regs_.F(inst.dest + i), regs_.F((which ? inst.src2 : inst.src1) + i));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Vec4Mov:
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
FMV(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i));
|
||||
FMV(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i));
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -191,47 +191,47 @@ void RiscVJitBackend::CompIR_VecArith(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Vec4Add:
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
FADD(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i));
|
||||
FADD(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i));
|
||||
break;
|
||||
|
||||
case IROp::Vec4Sub:
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
FSUB(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i));
|
||||
FSUB(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i));
|
||||
break;
|
||||
|
||||
case IROp::Vec4Mul:
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
FMUL(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i));
|
||||
FMUL(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i));
|
||||
break;
|
||||
|
||||
case IROp::Vec4Div:
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
FDIV(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i));
|
||||
FDIV(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i));
|
||||
break;
|
||||
|
||||
case IROp::Vec4Scale:
|
||||
fpr.SpillLockFPR(inst.src2);
|
||||
fpr.MapFPR(inst.src2);
|
||||
fpr.Map4DirtyIn(inst.dest, inst.src1);
|
||||
regs_.SpillLockFPR(inst.src2);
|
||||
regs_.MapFPR(inst.src2);
|
||||
regs_.MapFPR4DirtyIn(inst.dest, inst.src1);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
FMUL(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i), fpr.R(inst.src2));
|
||||
FMUL(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i), regs_.F(inst.src2));
|
||||
break;
|
||||
|
||||
case IROp::Vec4Neg:
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
FNEG(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i));
|
||||
FNEG(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i));
|
||||
break;
|
||||
|
||||
case IROp::Vec4Abs:
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
FABS(32, fpr.R(inst.dest + i), fpr.R(inst.src1 + i));
|
||||
FABS(32, regs_.F(inst.dest + i), regs_.F(inst.src1 + i));
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -246,32 +246,32 @@ void RiscVJitBackend::CompIR_VecHoriz(IRInst inst) {
|
||||
switch (inst.op) {
|
||||
case IROp::Vec4Dot:
|
||||
// TODO: Maybe some option to call the slow accurate mode?
|
||||
fpr.SpillLockFPR(inst.dest);
|
||||
regs_.SpillLockFPR(inst.dest);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
fpr.SpillLockFPR(inst.src1 + i);
|
||||
fpr.SpillLockFPR(inst.src2 + i);
|
||||
regs_.SpillLockFPR(inst.src1 + i);
|
||||
regs_.SpillLockFPR(inst.src2 + i);
|
||||
}
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
fpr.MapFPR(inst.src1 + i);
|
||||
fpr.MapFPR(inst.src2 + i);
|
||||
regs_.MapFPR(inst.src1 + i);
|
||||
regs_.MapFPR(inst.src2 + i);
|
||||
}
|
||||
fpr.MapFPR(inst.dest, MIPSMap::NOINIT);
|
||||
regs_.MapFPR(inst.dest, MIPSMap::NOINIT);
|
||||
|
||||
if ((inst.dest < inst.src1 + 4 && inst.dest >= inst.src1) || (inst.dest < inst.src2 + 4 && inst.dest >= inst.src2)) {
|
||||
// This means inst.dest overlaps one of src1 or src2. We have to do that one first.
|
||||
// Technically this may impact -0.0 and such, but dots accurately need to be aligned anyway.
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (inst.dest == inst.src1 + i || inst.dest == inst.src2 + i)
|
||||
FMUL(32, fpr.R(inst.dest), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i));
|
||||
FMUL(32, regs_.F(inst.dest), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i));
|
||||
}
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (inst.dest != inst.src1 + i && inst.dest != inst.src2 + i)
|
||||
FMADD(32, fpr.R(inst.dest), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i), fpr.R(inst.dest));
|
||||
FMADD(32, regs_.F(inst.dest), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i), regs_.F(inst.dest));
|
||||
}
|
||||
} else {
|
||||
FMUL(32, fpr.R(inst.dest), fpr.R(inst.src1), fpr.R(inst.src2));
|
||||
FMUL(32, regs_.F(inst.dest), regs_.F(inst.src1), regs_.F(inst.src2));
|
||||
for (int i = 1; i < 4; ++i)
|
||||
FMADD(32, fpr.R(inst.dest), fpr.R(inst.src1 + i), fpr.R(inst.src2 + i), fpr.R(inst.dest));
|
||||
FMADD(32, regs_.F(inst.dest), regs_.F(inst.src1 + i), regs_.F(inst.src2 + i), regs_.F(inst.dest));
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -292,14 +292,14 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) {
|
||||
break;
|
||||
|
||||
case IROp::Vec4Unpack8To32:
|
||||
fpr.SpillLockFPR(inst.src1);
|
||||
regs_.SpillLockFPR(inst.src1);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
fpr.SpillLockFPR(inst.dest + i);
|
||||
fpr.MapFPR(inst.src1);
|
||||
regs_.SpillLockFPR(inst.dest + i);
|
||||
regs_.MapFPR(inst.src1);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
fpr.MapFPR(inst.dest + i, MIPSMap::NOINIT);
|
||||
regs_.MapFPR(inst.dest + i, MIPSMap::NOINIT);
|
||||
|
||||
FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1));
|
||||
FMV(FMv::X, FMv::W, SCRATCH2, regs_.F(inst.src1));
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
// Mask using walls.
|
||||
if (i != 0) {
|
||||
@@ -308,49 +308,49 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) {
|
||||
} else {
|
||||
SLLI(SCRATCH1, SCRATCH2, 24);
|
||||
}
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest + i), SCRATCH1);
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Vec2Unpack16To32:
|
||||
fpr.SpillLockFPR(inst.src1);
|
||||
regs_.SpillLockFPR(inst.src1);
|
||||
for (int i = 0; i < 2; ++i)
|
||||
fpr.SpillLockFPR(inst.dest + i);
|
||||
fpr.MapFPR(inst.src1);
|
||||
regs_.SpillLockFPR(inst.dest + i);
|
||||
regs_.MapFPR(inst.src1);
|
||||
for (int i = 0; i < 2; ++i)
|
||||
fpr.MapFPR(inst.dest + i, MIPSMap::NOINIT);
|
||||
regs_.MapFPR(inst.dest + i, MIPSMap::NOINIT);
|
||||
|
||||
FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1));
|
||||
FMV(FMv::X, FMv::W, SCRATCH2, regs_.F(inst.src1));
|
||||
SLLI(SCRATCH1, SCRATCH2, 16);
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1);
|
||||
SRLI(SCRATCH1, SCRATCH2, 16);
|
||||
SLLI(SCRATCH1, SCRATCH1, 16);
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest + 1), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest + 1), SCRATCH1);
|
||||
break;
|
||||
|
||||
case IROp::Vec4DuplicateUpperBitsAndShift1:
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1 + i));
|
||||
FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1 + i));
|
||||
SRLIW(SCRATCH2, SCRATCH1, 8);
|
||||
OR(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
SRLIW(SCRATCH2, SCRATCH1, 16);
|
||||
OR(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
SRLIW(SCRATCH1, SCRATCH1, 1);
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest + i), SCRATCH1);
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Vec4Pack31To8:
|
||||
fpr.SpillLockFPR(inst.dest);
|
||||
regs_.SpillLockFPR(inst.dest);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
fpr.SpillLockFPR(inst.src1 + i);
|
||||
fpr.MapFPR(inst.src1 + i);
|
||||
regs_.SpillLockFPR(inst.src1 + i);
|
||||
regs_.MapFPR(inst.src1 + i);
|
||||
}
|
||||
fpr.MapFPR(inst.dest, MIPSMap::NOINIT);
|
||||
regs_.MapFPR(inst.dest, MIPSMap::NOINIT);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1 + i));
|
||||
FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1 + i));
|
||||
SRLI(SCRATCH1, SCRATCH1, 23);
|
||||
if (i == 0) {
|
||||
ANDI(SCRATCH2, SCRATCH1, 0xFF);
|
||||
@@ -361,13 +361,13 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) {
|
||||
}
|
||||
}
|
||||
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH2);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH2);
|
||||
break;
|
||||
|
||||
case IROp::Vec2Pack32To16:
|
||||
fpr.MapDirtyInIn(inst.dest, inst.src1, inst.src1 + 1);
|
||||
FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1));
|
||||
FMV(FMv::X, FMv::W, SCRATCH2, fpr.R(inst.src1 + 1));
|
||||
regs_.MapFPRDirtyInIn(inst.dest, inst.src1, inst.src1 + 1);
|
||||
FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1));
|
||||
FMV(FMv::X, FMv::W, SCRATCH2, regs_.F(inst.src1 + 1));
|
||||
// Keep in mind, this was sign-extended, so we have to zero the upper.
|
||||
SLLI(SCRATCH1, SCRATCH1, XLEN - 32);
|
||||
// Now we just set (SCRATCH2 & 0xFFFF0000) | SCRATCH1.
|
||||
@@ -377,7 +377,7 @@ void RiscVJitBackend::CompIR_VecPack(IRInst inst) {
|
||||
SLLI(SCRATCH2, SCRATCH2, 16);
|
||||
OR(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
// Okay, to the floating point register.
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -391,9 +391,9 @@ void RiscVJitBackend::CompIR_VecClamp(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Vec4ClampToZero:
|
||||
fpr.Map(inst);
|
||||
regs_.Map(inst);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
FMV(FMv::X, FMv::W, SCRATCH1, fpr.R(inst.src1 + i));
|
||||
FMV(FMv::X, FMv::W, SCRATCH1, regs_.F(inst.src1 + i));
|
||||
SRAIW(SCRATCH2, SCRATCH1, 31);
|
||||
if (cpu_info.RiscV_Zbb) {
|
||||
ANDN(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
@@ -401,7 +401,7 @@ void RiscVJitBackend::CompIR_VecClamp(IRInst inst) {
|
||||
NOT(SCRATCH2, SCRATCH2);
|
||||
AND(SCRATCH1, SCRATCH1, SCRATCH2);
|
||||
}
|
||||
FMV(FMv::W, FMv::X, fpr.R(inst.dest + i), SCRATCH1);
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest + i), SCRATCH1);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ static constexpr int MIN_BLOCK_NORMAL_LEN = 16;
|
||||
static constexpr int MIN_BLOCK_EXIT_LEN = 8;
|
||||
|
||||
RiscVJitBackend::RiscVJitBackend(JitOptions &jitopt, IRBlockCache &blocks)
|
||||
: IRNativeBackend(blocks), jo(jitopt), gpr(&jo), fpr(&jo) {
|
||||
: IRNativeBackend(blocks), jo(jitopt), regs_(&jo) {
|
||||
// Automatically disable incompatible options.
|
||||
if (((intptr_t)Memory::base & 0x00000000FFFFFFFFUL) != 0) {
|
||||
jo.enablePointerify = false;
|
||||
@@ -43,8 +43,7 @@ RiscVJitBackend::RiscVJitBackend(JitOptions &jitopt, IRBlockCache &blocks)
|
||||
AllocCodeSpace(1024 * 1024 * 16);
|
||||
SetAutoCompress(true);
|
||||
|
||||
gpr.Init(this);
|
||||
fpr.Init(this);
|
||||
regs_.Init(this);
|
||||
}
|
||||
|
||||
RiscVJitBackend::~RiscVJitBackend() {
|
||||
@@ -76,23 +75,19 @@ bool RiscVJitBackend::CompileBlock(IRBlock *block, int block_num, bool preload)
|
||||
block->SetTargetOffset((int)GetOffset(blockStart));
|
||||
compilingBlockNum_ = block_num;
|
||||
|
||||
gpr.Start(block);
|
||||
fpr.Start(block);
|
||||
regs_.Start(block);
|
||||
|
||||
std::map<const u8 *, IRInst> addresses;
|
||||
for (int i = 0; i < block->GetNumInstructions(); ++i) {
|
||||
const IRInst &inst = block->GetInstructions()[i];
|
||||
gpr.SetIRIndex(i);
|
||||
fpr.SetIRIndex(i);
|
||||
regs_.SetIRIndex(i);
|
||||
// TODO: This might be a little wasteful when compiling if we're not debugging jit...
|
||||
addresses[GetCodePtr()] = inst;
|
||||
|
||||
CompileIRInst(inst);
|
||||
|
||||
if (jo.Disabled(JitDisable::REGALLOC_GPR))
|
||||
gpr.FlushAll();
|
||||
if (jo.Disabled(JitDisable::REGALLOC_FPR))
|
||||
fpr.FlushAll();
|
||||
if (jo.Disabled(JitDisable::REGALLOC_GPR) || jo.Disabled(JitDisable::REGALLOC_FPR))
|
||||
regs_.FlushAll(jo.Disabled(JitDisable::REGALLOC_GPR), jo.Disabled(JitDisable::REGALLOC_FPR));
|
||||
|
||||
// Safety check, in case we get a bunch of really large jit ops without a lot of branching.
|
||||
if (GetSpaceLeft() < 0x800) {
|
||||
@@ -252,8 +247,7 @@ void RiscVJitBackend::CompIR_Interpret(IRInst inst) {
|
||||
}
|
||||
|
||||
void RiscVJitBackend::FlushAll() {
|
||||
gpr.FlushAll();
|
||||
fpr.FlushAll();
|
||||
regs_.FlushAll();
|
||||
}
|
||||
|
||||
bool RiscVJitBackend::DescribeCodePtr(const u8 *ptr, std::string &name) const {
|
||||
@@ -355,12 +349,12 @@ void RiscVJitBackend::NormalizeSrc12(IRInst inst, RiscVReg *lhs, RiscVReg *rhs,
|
||||
RiscVReg RiscVJitBackend::NormalizeR(IRReg rs, IRReg rd, RiscVReg tempReg) {
|
||||
// For proper compare, we must sign extend so they both match or don't match.
|
||||
// But don't change pointers, in case one is SP (happens in LittleBigPlanet.)
|
||||
if (gpr.IsGPRImm(rs) && gpr.GetGPRImm(rs) == 0) {
|
||||
if (regs_.IsGPRImm(rs) && regs_.GetGPRImm(rs) == 0) {
|
||||
return R_ZERO;
|
||||
} else if (gpr.IsGPRMappedAsPointer(rs) || rs == rd) {
|
||||
return gpr.Normalize32(rs, tempReg);
|
||||
} else if (regs_.IsGPRMappedAsPointer(rs) || rs == rd) {
|
||||
return regs_.Normalize32(rs, tempReg);
|
||||
} else {
|
||||
return gpr.Normalize32(rs);
|
||||
return regs_.Normalize32(rs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -31,8 +31,10 @@ using namespace RiscVJitConstants;
|
||||
|
||||
RiscVRegCache::RiscVRegCache(MIPSComp::JitOptions *jo)
|
||||
: IRNativeRegCacheBase(jo) {
|
||||
// TODO: Move to using for FPRs and VPRs too?
|
||||
config_.totalNativeRegs = NUM_RVREG;
|
||||
// TODO: Update these when using RISC-V V.
|
||||
config_.totalNativeRegs = NUM_RVGPR + NUM_RVFPR;
|
||||
config_.mapUseVRegs = false;
|
||||
config_.mapSIMD = false;
|
||||
}
|
||||
|
||||
void RiscVRegCache::Init(RiscVEmitter *emitter) {
|
||||
@@ -54,23 +56,40 @@ void RiscVRegCache::SetupInitialRegs() {
|
||||
}
|
||||
|
||||
const int *RiscVRegCache::GetAllocationOrder(MIPSLoc type, int &count, int &base) const {
|
||||
_assert_(type == MIPSLoc::REG);
|
||||
// X8 and X9 are the most ideal for static alloc because they can be used with compression.
|
||||
// Otherwise we stick to saved regs - might not be necessary.
|
||||
static const int allocationOrder[] = {
|
||||
X8, X9, X12, X13, X14, X15, X5, X6, X7, X16, X17, X18, X19, X20, X21, X22, X23, X28, X29, X30, X31,
|
||||
};
|
||||
static const int allocationOrderStaticAlloc[] = {
|
||||
X12, X13, X14, X15, X5, X6, X7, X16, X17, X21, X22, X23, X28, X29, X30, X31,
|
||||
};
|
||||
|
||||
base = X0;
|
||||
if (jo_->useStaticAlloc) {
|
||||
count = ARRAY_SIZE(allocationOrderStaticAlloc);
|
||||
return allocationOrderStaticAlloc;
|
||||
} else {
|
||||
|
||||
if (type == MIPSLoc::REG) {
|
||||
// X8 and X9 are the most ideal for static alloc because they can be used with compression.
|
||||
// Otherwise we stick to saved regs - might not be necessary.
|
||||
static const int allocationOrder[] = {
|
||||
X8, X9, X12, X13, X14, X15, X5, X6, X7, X16, X17, X18, X19, X20, X21, X22, X23, X28, X29, X30, X31,
|
||||
};
|
||||
static const int allocationOrderStaticAlloc[] = {
|
||||
X12, X13, X14, X15, X5, X6, X7, X16, X17, X21, X22, X23, X28, X29, X30, X31,
|
||||
};
|
||||
|
||||
if (jo_->useStaticAlloc) {
|
||||
count = ARRAY_SIZE(allocationOrderStaticAlloc);
|
||||
return allocationOrderStaticAlloc;
|
||||
} else {
|
||||
count = ARRAY_SIZE(allocationOrder);
|
||||
return allocationOrder;
|
||||
}
|
||||
} else if (type == MIPSLoc::FREG) {
|
||||
// F8 through F15 are used for compression, so they are great.
|
||||
// TODO: Maybe we could remove some saved regs since we rarely need that many? Or maybe worth it?
|
||||
static const int allocationOrder[] = {
|
||||
F8, F9, F10, F11, F12, F13, F14, F15,
|
||||
F0, F1, F2, F3, F4, F5, F6, F7,
|
||||
F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31,
|
||||
};
|
||||
|
||||
count = ARRAY_SIZE(allocationOrder);
|
||||
return allocationOrder;
|
||||
} else {
|
||||
_assert_msg_(false, "Allocation order not yet implemented");
|
||||
count = 0;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,14 +135,18 @@ void RiscVRegCache::EmitSaveStaticRegisters() {
|
||||
|
||||
void RiscVRegCache::FlushBeforeCall() {
|
||||
// These registers are not preserved by function calls.
|
||||
// They match between X0 and F0, conveniently.
|
||||
for (int i = 5; i <= 7; ++i) {
|
||||
FlushNativeReg(i);
|
||||
FlushNativeReg(X0 + i);
|
||||
FlushNativeReg(F0 + i);
|
||||
}
|
||||
for (int i = 10; i <= 17; ++i) {
|
||||
FlushNativeReg(i);
|
||||
FlushNativeReg(X0 + i);
|
||||
FlushNativeReg(F0 + i);
|
||||
}
|
||||
for (int i = 28; i <= 31; ++i) {
|
||||
FlushNativeReg(i);
|
||||
FlushNativeReg(X0 + i);
|
||||
FlushNativeReg(F0 + i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +255,7 @@ RiscVReg RiscVRegCache::MapGPRAsPointer(IRReg reg) {
|
||||
return (RiscVReg)MapNativeRegAsPointer(reg);
|
||||
}
|
||||
|
||||
void RiscVRegCache::MapDirtyIn(IRReg rd, IRReg rs, MapType type) {
|
||||
void RiscVRegCache::MapGPRDirtyIn(IRReg rd, IRReg rs, MapType type) {
|
||||
SpillLockGPR(rd, rs);
|
||||
bool load = type == MapType::ALWAYS_LOAD || rd == rs;
|
||||
MIPSMap norm32 = type == MapType::AVOID_LOAD_MARK_NORM32 ? MIPSMap::MARK_NORM32 : MIPSMap::INIT;
|
||||
@@ -241,7 +264,7 @@ void RiscVRegCache::MapDirtyIn(IRReg rd, IRReg rs, MapType type) {
|
||||
ReleaseSpillLockGPR(rd, rs);
|
||||
}
|
||||
|
||||
void RiscVRegCache::MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, MapType type) {
|
||||
void RiscVRegCache::MapGPRDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, MapType type) {
|
||||
SpillLockGPR(rd1, rd2, rs, rt);
|
||||
bool load1 = type == MapType::ALWAYS_LOAD || (rd1 == rs || rd1 == rt);
|
||||
bool load2 = type == MapType::ALWAYS_LOAD || (rd2 == rs || rd2 == rt);
|
||||
@@ -253,6 +276,61 @@ void RiscVRegCache::MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt,
|
||||
ReleaseSpillLockGPR(rd1, rd2, rs, rt);
|
||||
}
|
||||
|
||||
RiscVReg RiscVRegCache::MapFPR(IRReg mipsReg, MIPSMap mapFlags) {
|
||||
_dbg_assert_(IsValidFPR(mipsReg));
|
||||
_dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::MEM || mr[mipsReg + 32].loc == MIPSLoc::FREG);
|
||||
|
||||
IRNativeReg nreg = MapNativeReg(MIPSLoc::FREG, mipsReg + 32, 1, mapFlags);
|
||||
if (nreg != -1)
|
||||
return (RiscVReg)nreg;
|
||||
return INVALID_REG;
|
||||
}
|
||||
|
||||
void RiscVRegCache::MapFPRDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad) {
|
||||
SpillLockFPR(rd, rs, rt);
|
||||
bool load = !avoidLoad || (rd == rs || rd == rt);
|
||||
MapFPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT);
|
||||
MapFPR(rt);
|
||||
MapFPR(rs);
|
||||
ReleaseSpillLockFPR(rd, rs, rt);
|
||||
}
|
||||
|
||||
RiscVReg RiscVRegCache::MapFPRDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad) {
|
||||
SpillLockFPR(rd, rs);
|
||||
bool load = !avoidLoad || rd == rs;
|
||||
MapFPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT);
|
||||
MapFPR(rs);
|
||||
RiscVReg temp = (RiscVReg)AllocateReg(MIPSLoc::FREG);
|
||||
ReleaseSpillLockFPR(rd, rs);
|
||||
return temp;
|
||||
}
|
||||
|
||||
void RiscVRegCache::MapFPR4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad) {
|
||||
for (int i = 0; i < 4; ++i)
|
||||
SpillLockFPR(rdbase + i, rsbase + i);
|
||||
bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
MapFPR(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
MapFPR(rsbase + i);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
ReleaseSpillLockFPR(rdbase + i, rsbase + i);
|
||||
}
|
||||
|
||||
RiscVReg RiscVRegCache::MapFPR4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad) {
|
||||
for (int i = 0; i < 4; ++i)
|
||||
SpillLockFPR(rdbase + i, rsbase + i);
|
||||
bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
MapFPR(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
MapFPR(rsbase + i);
|
||||
RiscVReg temp = (RiscVReg)AllocateReg(MIPSLoc::FREG);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
ReleaseSpillLockFPR(rdbase + i, rsbase + i);
|
||||
return temp;
|
||||
}
|
||||
|
||||
void RiscVRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) {
|
||||
RiscVReg r = (RiscVReg)(X0 + nreg);
|
||||
_assert_(r >= X0 && r <= X31);
|
||||
@@ -286,32 +364,54 @@ void RiscVRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) {
|
||||
|
||||
void RiscVRegCache::LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) {
|
||||
RiscVReg r = (RiscVReg)(X0 + nreg);
|
||||
_dbg_assert_(r > X0 && r <= X31);
|
||||
_dbg_assert_(r > X0);
|
||||
_dbg_assert_(first != MIPS_REG_ZERO);
|
||||
// Multilane not yet supported.
|
||||
_assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO));
|
||||
if (lanes == 1)
|
||||
emit_->LW(r, CTXREG, GetMipsRegOffset(first));
|
||||
else if (lanes == 2)
|
||||
emit_->LD(r, CTXREG, GetMipsRegOffset(first));
|
||||
else
|
||||
_assert_(false);
|
||||
nr[nreg].normalized32 = true;
|
||||
if (r <= X31) {
|
||||
// Multilane not yet supported.
|
||||
_assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO));
|
||||
if (lanes == 1)
|
||||
emit_->LW(r, CTXREG, GetMipsRegOffset(first));
|
||||
else if (lanes == 2)
|
||||
emit_->LD(r, CTXREG, GetMipsRegOffset(first));
|
||||
else
|
||||
_assert_(false);
|
||||
nr[nreg].normalized32 = true;
|
||||
} else {
|
||||
_dbg_assert_(r >= F0 && r <= F31);
|
||||
// Multilane not yet supported.
|
||||
_assert_(lanes == 1);
|
||||
if (mr[first].loc == MIPSLoc::FREG) {
|
||||
emit_->FL(32, r, CTXREG, GetMipsRegOffset(first));
|
||||
} else {
|
||||
_assert_msg_(mr[first].loc == MIPSLoc::FREG, "Cannot store this type: %d", (int)mr[first].loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RiscVRegCache::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) {
|
||||
RiscVReg r = (RiscVReg)(X0 + nreg);
|
||||
_dbg_assert_(r > X0 && r <= X31);
|
||||
_dbg_assert_(r > X0);
|
||||
_dbg_assert_(first != MIPS_REG_ZERO);
|
||||
// Multilane not yet supported.
|
||||
_assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO));
|
||||
_assert_(mr[first].loc == MIPSLoc::REG || mr[first].loc == MIPSLoc::REG_IMM);
|
||||
if (lanes == 1)
|
||||
emit_->SW(r, CTXREG, GetMipsRegOffset(first));
|
||||
else if (lanes == 2)
|
||||
emit_->SD(r, CTXREG, GetMipsRegOffset(first));
|
||||
else
|
||||
_assert_(false);
|
||||
if (r <= X31) {
|
||||
// Multilane not yet supported.
|
||||
_assert_(lanes == 1 || (lanes == 2 && first == IRREG_LO));
|
||||
_assert_(mr[first].loc == MIPSLoc::REG || mr[first].loc == MIPSLoc::REG_IMM);
|
||||
if (lanes == 1)
|
||||
emit_->SW(r, CTXREG, GetMipsRegOffset(first));
|
||||
else if (lanes == 2)
|
||||
emit_->SD(r, CTXREG, GetMipsRegOffset(first));
|
||||
else
|
||||
_assert_(false);
|
||||
} else {
|
||||
_dbg_assert_(r >= F0 && r <= F31);
|
||||
// Multilane not yet supported.
|
||||
_assert_(lanes == 1);
|
||||
if (mr[first].loc == MIPSLoc::FREG) {
|
||||
emit_->FS(32, r, CTXREG, GetMipsRegOffset(first));
|
||||
} else {
|
||||
_assert_msg_(mr[first].loc == MIPSLoc::FREG, "Cannot store this type: %d", (int)mr[first].loc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RiscVRegCache::SetNativeRegValue(IRNativeReg nreg, uint32_t imm) {
|
||||
@@ -326,7 +426,7 @@ void RiscVRegCache::SetNativeRegValue(IRNativeReg nreg, uint32_t imm) {
|
||||
}
|
||||
|
||||
void RiscVRegCache::StoreRegValue(IRReg mreg, uint32_t imm) {
|
||||
_assert_(mreg != MIPS_REG_ZERO);
|
||||
_assert_(IsValidGPRNoZero(mreg));
|
||||
// Try to optimize using a different reg.
|
||||
RiscVReg storeReg = INVALID_REG;
|
||||
|
||||
@@ -382,3 +482,14 @@ RiscVReg RiscVRegCache::RPtr(IRReg mipsReg) {
|
||||
return INVALID_REG; // BAAAD
|
||||
}
|
||||
}
|
||||
|
||||
RiscVReg RiscVRegCache::F(IRReg mipsReg) {
|
||||
_dbg_assert_(IsValidFPR(mipsReg));
|
||||
_dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::FREG);
|
||||
if (mr[mipsReg + 32].loc == MIPSLoc::FREG) {
|
||||
return (RiscVReg)mr[mipsReg + 32].nReg;
|
||||
} else {
|
||||
ERROR_LOG_REPORT(JIT, "Reg %i not in riscv reg", mipsReg);
|
||||
return INVALID_REG; // BAAAD
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,18 +56,29 @@ public:
|
||||
RiscVGen::RiscVReg MapGPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT);
|
||||
RiscVGen::RiscVReg MapGPRAsPointer(IRReg reg);
|
||||
|
||||
void MapGPRDirtyIn(IRReg rd, IRReg rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD);
|
||||
void MapGPRDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD);
|
||||
|
||||
// Returns a RISC-V register containing the requested MIPS register.
|
||||
RiscVGen::RiscVReg MapFPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT);
|
||||
|
||||
void MapFPRDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad = true);
|
||||
RiscVGen::RiscVReg MapFPRDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad = true);
|
||||
void MapFPR4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad = true);
|
||||
RiscVGen::RiscVReg MapFPR4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad = true);
|
||||
|
||||
bool IsNormalized32(IRReg reg);
|
||||
|
||||
// Copies to another reg if specified, otherwise same reg.
|
||||
RiscVGen::RiscVReg Normalize32(IRReg reg, RiscVGen::RiscVReg destReg = RiscVGen::INVALID_REG);
|
||||
void MapDirtyIn(IRReg rd, IRReg rs, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD);
|
||||
void MapDirtyDirtyInIn(IRReg rd1, IRReg rd2, IRReg rs, IRReg rt, RiscVJitConstants::MapType type = RiscVJitConstants::MapType::AVOID_LOAD);
|
||||
|
||||
void FlushBeforeCall();
|
||||
|
||||
RiscVGen::RiscVReg GetAndLockTempR();
|
||||
|
||||
RiscVGen::RiscVReg R(IRReg preg); // Returns a cached register, while checking that it's NOT mapped as a pointer
|
||||
RiscVGen::RiscVReg RPtr(IRReg preg); // Returns a cached register, if it has been mapped as a pointer
|
||||
RiscVGen::RiscVReg F(IRReg preg);
|
||||
|
||||
// These are called once on startup to generate functions, that you should then call.
|
||||
void EmitLoadStaticRegisters();
|
||||
@@ -88,6 +99,8 @@ private:
|
||||
RiscVGen::RiscVEmitter *emit_ = nullptr;
|
||||
|
||||
enum {
|
||||
NUM_RVREG = 32,
|
||||
NUM_RVGPR = 32,
|
||||
NUM_RVFPR = 32,
|
||||
NUM_RVVPR = 32,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
// Copyright (c) 2023- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#ifndef offsetof
|
||||
#include <cstddef>
|
||||
#endif
|
||||
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Core/MIPS/IR/IRInst.h"
|
||||
#include "Core/MIPS/IR/IRAnalysis.h"
|
||||
#include "Core/MIPS/RiscV/RiscVRegCacheFPU.h"
|
||||
#include "Core/MIPS/JitCommon/JitState.h"
|
||||
#include "Core/Reporting.h"
|
||||
|
||||
using namespace RiscVGen;
|
||||
using namespace RiscVJitConstants;
|
||||
|
||||
RiscVRegCacheFPU::RiscVRegCacheFPU(MIPSComp::JitOptions *jo)
|
||||
: IRNativeRegCacheBase(jo) {
|
||||
config_.totalNativeRegs = NUM_RVFPUREG;
|
||||
// TODO: When using RISC-V V.
|
||||
config_.mapUseVRegs = false;
|
||||
config_.mapSIMD = false;
|
||||
}
|
||||
|
||||
void RiscVRegCacheFPU::Init(RiscVEmitter *emitter) {
|
||||
emit_ = emitter;
|
||||
}
|
||||
|
||||
const int *RiscVRegCacheFPU::GetAllocationOrder(MIPSLoc type, int &count, int &base) const {
|
||||
_assert_(type == MIPSLoc::FREG);
|
||||
// F8 through F15 are used for compression, so they are great.
|
||||
// TODO: Maybe we could remove some saved regs since we rarely need that many? Or maybe worth it?
|
||||
static const int allocationOrder[] = {
|
||||
F8, F9, F10, F11, F12, F13, F14, F15,
|
||||
F0, F1, F2, F3, F4, F5, F6, F7,
|
||||
F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31,
|
||||
};
|
||||
|
||||
count = ARRAY_SIZE(allocationOrder);
|
||||
base = F0;
|
||||
return allocationOrder;
|
||||
}
|
||||
|
||||
RiscVReg RiscVRegCacheFPU::MapFPR(IRReg mipsReg, MIPSMap mapFlags) {
|
||||
_dbg_assert_(IsValidFPR(mipsReg));
|
||||
_dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::MEM || mr[mipsReg + 32].loc == MIPSLoc::FREG);
|
||||
|
||||
IRNativeReg nreg = MapNativeReg(MIPSLoc::FREG, mipsReg + 32, 1, mapFlags);
|
||||
if (nreg != -1)
|
||||
return (RiscVReg)(F0 + nreg);
|
||||
return INVALID_REG;
|
||||
}
|
||||
|
||||
void RiscVRegCacheFPU::MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad) {
|
||||
SpillLockFPR(rd, rs, rt);
|
||||
bool load = !avoidLoad || (rd == rs || rd == rt);
|
||||
MapFPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT);
|
||||
MapFPR(rt);
|
||||
MapFPR(rs);
|
||||
ReleaseSpillLockFPR(rd, rs, rt);
|
||||
}
|
||||
|
||||
RiscVReg RiscVRegCacheFPU::MapDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad) {
|
||||
SpillLockFPR(rd, rs);
|
||||
bool load = !avoidLoad || rd == rs;
|
||||
MapFPR(rd, load ? MIPSMap::DIRTY : MIPSMap::NOINIT);
|
||||
MapFPR(rs);
|
||||
RiscVReg temp = (RiscVReg)(F0 + AllocateReg(MIPSLoc::FREG));
|
||||
ReleaseSpillLockFPR(rd, rs);
|
||||
return temp;
|
||||
}
|
||||
|
||||
void RiscVRegCacheFPU::Map4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad) {
|
||||
for (int i = 0; i < 4; ++i)
|
||||
SpillLockFPR(rdbase + i, rsbase + i);
|
||||
bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
MapFPR(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
MapFPR(rsbase + i);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
ReleaseSpillLockFPR(rdbase + i, rsbase + i);
|
||||
}
|
||||
|
||||
RiscVReg RiscVRegCacheFPU::Map4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad) {
|
||||
for (int i = 0; i < 4; ++i)
|
||||
SpillLockFPR(rdbase + i, rsbase + i);
|
||||
bool load = !avoidLoad || (rdbase < rsbase + 4 && rdbase + 4 > rsbase);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
MapFPR(rdbase + i, load ? MIPSMap::DIRTY : MIPSMap::NOINIT);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
MapFPR(rsbase + i);
|
||||
RiscVReg temp = (RiscVReg)(F0 + AllocateReg(MIPSLoc::FREG));
|
||||
for (int i = 0; i < 4; ++i)
|
||||
ReleaseSpillLockFPR(rdbase + i, rsbase + i);
|
||||
return temp;
|
||||
}
|
||||
|
||||
void RiscVRegCacheFPU::LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) {
|
||||
RiscVReg r = (RiscVReg)(F0 + nreg);
|
||||
_dbg_assert_(r >= F0 && r <= F31);
|
||||
// Multilane not yet supported.
|
||||
_assert_(lanes == 1);
|
||||
if (mr[first].loc == MIPSLoc::FREG) {
|
||||
emit_->FL(32, r, CTXREG, GetMipsRegOffset(first));
|
||||
} else {
|
||||
_assert_msg_(mr[first].loc == MIPSLoc::FREG, "Cannot store this type: %d", (int)mr[first].loc);
|
||||
}
|
||||
}
|
||||
|
||||
void RiscVRegCacheFPU::StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) {
|
||||
RiscVReg r = (RiscVReg)(F0 + nreg);
|
||||
_dbg_assert_(r >= F0 && r <= F31);
|
||||
// Multilane not yet supported.
|
||||
_assert_(lanes == 1);
|
||||
if (mr[first].loc == MIPSLoc::FREG) {
|
||||
emit_->FS(32, r, CTXREG, GetMipsRegOffset(first));
|
||||
} else {
|
||||
_assert_msg_(mr[first].loc == MIPSLoc::FREG, "Cannot store this type: %d", (int)mr[first].loc);
|
||||
}
|
||||
}
|
||||
|
||||
void RiscVRegCacheFPU::SetNativeRegValue(IRNativeReg nreg, uint32_t imm) {
|
||||
_assert_msg_(false, "Set float to imm is unsupported");
|
||||
}
|
||||
|
||||
void RiscVRegCacheFPU::StoreRegValue(IRReg mreg, uint32_t imm) {
|
||||
_assert_msg_(false, "Storing imms to floats is unsupported");
|
||||
}
|
||||
|
||||
void RiscVRegCacheFPU::FlushBeforeCall() {
|
||||
// These registers are not preserved by function calls.
|
||||
for (int i = 0; i <= 7; ++i) {
|
||||
FlushNativeReg(i);
|
||||
}
|
||||
for (int i = 10; i <= 17; ++i) {
|
||||
FlushNativeReg(i);
|
||||
}
|
||||
for (int i = 28; i <= 31; ++i) {
|
||||
FlushNativeReg(i);
|
||||
}
|
||||
}
|
||||
|
||||
RiscVReg RiscVRegCacheFPU::R(IRReg mipsReg) {
|
||||
_dbg_assert_(IsValidFPR(mipsReg));
|
||||
_dbg_assert_(mr[mipsReg + 32].loc == MIPSLoc::FREG);
|
||||
if (mr[mipsReg + 32].loc == MIPSLoc::FREG) {
|
||||
return (RiscVReg)(mr[mipsReg + 32].nReg + F0);
|
||||
} else {
|
||||
ERROR_LOG_REPORT(JIT, "Reg %i not in riscv reg", mipsReg);
|
||||
return INVALID_REG; // BAAAD
|
||||
}
|
||||
}
|
||||
|
||||
void RiscVRegCacheFPU::SetupInitialRegs() {
|
||||
IRNativeRegCacheBase::SetupInitialRegs();
|
||||
|
||||
// TODO: Move to a shared cache?
|
||||
mrInitial_[0].loc = MIPSLoc::MEM;
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// Copyright (c) 2023- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common/RiscVEmitter.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/MIPS/RiscV/RiscVRegCache.h"
|
||||
|
||||
namespace MIPSComp {
|
||||
struct JitOptions;
|
||||
}
|
||||
|
||||
class RiscVRegCacheFPU : public IRNativeRegCacheBase {
|
||||
public:
|
||||
RiscVRegCacheFPU(MIPSComp::JitOptions *jo);
|
||||
|
||||
void Init(RiscVGen::RiscVEmitter *emitter);
|
||||
|
||||
// Returns a RISC-V register containing the requested MIPS register.
|
||||
RiscVGen::RiscVReg MapFPR(IRReg reg, MIPSMap mapFlags = MIPSMap::INIT);
|
||||
|
||||
void MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt, bool avoidLoad = true);
|
||||
RiscVGen::RiscVReg MapDirtyInTemp(IRReg rd, IRReg rs, bool avoidLoad = true);
|
||||
void Map4DirtyIn(IRReg rdbase, IRReg rsbase, bool avoidLoad = true);
|
||||
RiscVGen::RiscVReg Map4DirtyInTemp(IRReg rdbase, IRReg rsbase, bool avoidLoad = true);
|
||||
void FlushBeforeCall();
|
||||
|
||||
RiscVGen::RiscVReg R(IRReg preg); // Returns a cached register
|
||||
|
||||
protected:
|
||||
void SetupInitialRegs() override;
|
||||
const int *GetAllocationOrder(MIPSLoc type, int &count, int &base) const override;
|
||||
|
||||
void LoadNativeReg(IRNativeReg nreg, IRReg first, int lanes) override;
|
||||
void StoreNativeReg(IRNativeReg nreg, IRReg first, int lanes) override;
|
||||
void SetNativeRegValue(IRNativeReg nreg, uint32_t imm) override;
|
||||
void StoreRegValue(IRReg mreg, uint32_t imm) override;
|
||||
|
||||
private:
|
||||
RiscVGen::RiscVEmitter *emit_ = nullptr;
|
||||
|
||||
enum {
|
||||
NUM_RVFPUREG = 32,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user