mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-14 10:09:32 +01:00
Merge pull request #18103 from unknownbrackets/arm64jit-more
Implement the remaining arm64jit IR ops
This commit is contained in:
7 files changed
+325
-50
No files matched your search
+84
-10
@@ -2234,6 +2234,45 @@ void ARM64FloatEmitter::FCVTU(ARM64Reg Rd, ARM64Reg Rn, RoundingMode round) {
|
||||
EmitConvertScalarToInt(Rd, Rn, round, true);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::FCVTZS(ARM64Reg Rd, ARM64Reg Rn, int scale) {
|
||||
if (IsScalar(Rd)) {
|
||||
int imm = (IsDouble(Rn) ? 64 : 32) * 2 - scale;
|
||||
Rd = DecodeReg(Rd);
|
||||
Rn = DecodeReg(Rn);
|
||||
|
||||
Write32((1 << 30) | (0 << 29) | (0x1F << 24) | (imm << 16) | (0x1F << 11) | (1 << 10) | (Rn << 5) | Rd);
|
||||
} else {
|
||||
bool sf = Is64Bit(Rd);
|
||||
u32 type = 0;
|
||||
if (IsDouble(Rd))
|
||||
type = 1;
|
||||
int rmode = 3;
|
||||
int opcode = 0;
|
||||
|
||||
Write32((sf << 31) | (0 << 29) | (0x1E << 24) | (type << 22) | (rmode << 19) | (opcode << 16) | (scale << 10) | (Rn << 5) | Rd);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::FCVTZU(ARM64Reg Rd, ARM64Reg Rn, int scale) {
|
||||
if (IsScalar(Rd)) {
|
||||
int imm = (IsDouble(Rn) ? 64 : 32) * 2 - scale;
|
||||
Rd = DecodeReg(Rd);
|
||||
Rn = DecodeReg(Rn);
|
||||
|
||||
Write32((1 << 30) | (1 << 29) | (0x1F << 24) | (imm << 16) | (0x1F << 11) | (1 << 10) | (Rn << 5) | Rd);
|
||||
} else {
|
||||
bool sf = Is64Bit(Rd);
|
||||
u32 type = 0;
|
||||
if (IsDouble(Rd))
|
||||
type = 1;
|
||||
int rmode = 3;
|
||||
int opcode = 1;
|
||||
|
||||
Write32((sf << 31) | (0 << 29) | (0x1E << 24) | (type << 22) | (rmode << 19) | (opcode << 16) | (scale << 10) | (Rn << 5) | Rd);
|
||||
}
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::EmitConversion2(bool sf, bool S, bool direction, u32 type, u32 rmode, u32 opcode, int scale, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Rd = DecodeReg(Rd);
|
||||
@@ -3098,6 +3137,14 @@ void ARM64FloatEmitter::FCVTZU(u8 size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Emit2RegMisc(IsQuad(Rd), 1, 2 | (size >> 6), 0x1B, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::FCVTZS(u8 size, ARM64Reg Rd, ARM64Reg Rn, int scale) {
|
||||
int imm = size * 2 - scale;
|
||||
EmitShiftImm(IsQuad(Rd), false, imm >> 3, imm & 7, 0x1F, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::FCVTZU(u8 size, ARM64Reg Rd, ARM64Reg Rn, int scale) {
|
||||
int imm = size * 2 - scale;
|
||||
EmitShiftImm(IsQuad(Rd), true, imm >> 3, imm & 7, 0x1F, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::FDIV(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitThreeSame(1, size >> 6, 0x1F, Rd, Rn, Rm);
|
||||
@@ -3539,22 +3586,38 @@ void ARM64FloatEmitter::UCVTF(ARM64Reg Rd, ARM64Reg Rn)
|
||||
|
||||
void ARM64FloatEmitter::SCVTF(ARM64Reg Rd, ARM64Reg Rn, int scale)
|
||||
{
|
||||
bool sf = Is64Bit(Rn);
|
||||
u32 type = 0;
|
||||
if (IsDouble(Rd))
|
||||
type = 1;
|
||||
if (IsScalar(Rn)) {
|
||||
int imm = (IsDouble(Rn) ? 64 : 32) * 2 - scale;
|
||||
Rd = DecodeReg(Rd);
|
||||
Rn = DecodeReg(Rn);
|
||||
|
||||
EmitConversion2(sf, 0, false, type, 0, 2, 64 - scale, Rd, Rn);
|
||||
Write32((1 << 30) | (0 << 29) | (0x1F << 24) | (imm << 16) | (0x1C << 11) | (1 << 10) | (Rn << 5) | Rd);
|
||||
} else {
|
||||
bool sf = Is64Bit(Rn);
|
||||
u32 type = 0;
|
||||
if (IsDouble(Rd))
|
||||
type = 1;
|
||||
|
||||
EmitConversion2(sf, 0, false, type, 0, 2, 64 - scale, Rd, Rn);
|
||||
}
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::UCVTF(ARM64Reg Rd, ARM64Reg Rn, int scale)
|
||||
{
|
||||
bool sf = Is64Bit(Rn);
|
||||
u32 type = 0;
|
||||
if (IsDouble(Rd))
|
||||
type = 1;
|
||||
if (IsScalar(Rn)) {
|
||||
int imm = (IsDouble(Rn) ? 64 : 32) * 2 - scale;
|
||||
Rd = DecodeReg(Rd);
|
||||
Rn = DecodeReg(Rn);
|
||||
|
||||
EmitConversion2(sf, 0, false, type, 0, 3, 64 - scale, Rd, Rn);
|
||||
Write32((1 << 30) | (1 << 29) | (0x1F << 24) | (imm << 16) | (0x1C << 11) | (1 << 10) | (Rn << 5) | Rd);
|
||||
} else {
|
||||
bool sf = Is64Bit(Rn);
|
||||
u32 type = 0;
|
||||
if (IsDouble(Rd))
|
||||
type = 1;
|
||||
|
||||
EmitConversion2(sf, 0, false, type, 0, 3, 64 - scale, Rd, Rn);
|
||||
}
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::FCMP(ARM64Reg Rn, ARM64Reg Rm)
|
||||
@@ -3684,6 +3747,12 @@ void ARM64FloatEmitter::USHLL2(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift)
|
||||
{
|
||||
USHLL(src_size, Rd, Rn, shift, true);
|
||||
}
|
||||
void ARM64FloatEmitter::SHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn) {
|
||||
SHLL(src_size, Rd, Rn, false);
|
||||
}
|
||||
void ARM64FloatEmitter::SHLL2(u8 src_size, ARM64Reg Rd, ARM64Reg Rn) {
|
||||
SHLL(src_size, Rd, Rn, true);
|
||||
}
|
||||
void ARM64FloatEmitter::SXTL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
SXTL(src_size, Rd, Rn, false);
|
||||
@@ -3723,6 +3792,11 @@ void ARM64FloatEmitter::USHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift,
|
||||
EmitShiftImm(upper, 1, imm >> 3, imm & 7, 0x14, Rd, Rn);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::SHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, bool upper) {
|
||||
_assert_msg_(src_size <= 32, "%s shift amount cannot be 64", __FUNCTION__);
|
||||
Emit2RegMisc(upper, 1, src_size >> 4, 0b10011, Rd, Rn);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::SHRN(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift, bool upper)
|
||||
{
|
||||
_assert_msg_(shift > 0, "%s shift amount must be greater than zero!", __FUNCTION__);
|
||||
|
||||
@@ -866,6 +866,8 @@ public:
|
||||
void FCVTN(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void FCVTZS(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void FCVTZU(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void FCVTZS(u8 size, ARM64Reg Rd, ARM64Reg Rn, int scale);
|
||||
void FCVTZU(u8 size, ARM64Reg Rd, ARM64Reg Rn, int scale);
|
||||
void FDIV(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void FMUL(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void FNEG(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
@@ -931,6 +933,8 @@ public:
|
||||
// and one that outputs to a scalar fp register.
|
||||
void FCVTS(ARM64Reg Rd, ARM64Reg Rn, RoundingMode round);
|
||||
void FCVTU(ARM64Reg Rd, ARM64Reg Rn, RoundingMode round);
|
||||
void FCVTZS(ARM64Reg Rd, ARM64Reg Rn, int scale);
|
||||
void FCVTZU(ARM64Reg Rd, ARM64Reg Rn, int scale);
|
||||
|
||||
// Scalar convert int to float. No rounding mode specifier necessary.
|
||||
void SCVTF(ARM64Reg Rd, ARM64Reg Rn);
|
||||
@@ -976,6 +980,9 @@ public:
|
||||
void SSHLL2(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift);
|
||||
void USHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift);
|
||||
void USHLL2(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift);
|
||||
// Shift == src_size for these.
|
||||
void SHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void SHLL2(u8 src_size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void SHRN(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift);
|
||||
void SHRN2(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift);
|
||||
void SXTL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
@@ -1034,6 +1041,7 @@ private:
|
||||
|
||||
void SSHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift, bool upper);
|
||||
void USHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift, bool upper);
|
||||
void SHLL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, bool upper);
|
||||
void SHRN(u8 dest_size, ARM64Reg Rd, ARM64Reg Rn, u32 shift, bool upper);
|
||||
void SXTL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, bool upper);
|
||||
void UXTL(u8 src_size, ARM64Reg Rd, ARM64Reg Rn, bool upper);
|
||||
|
||||
@@ -374,7 +374,15 @@ void Arm64JitBackend::CompIR_FCvt(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::FCvtWS:
|
||||
CompIR_Generic(inst);
|
||||
// TODO: Unfortunately, we don't currently have the hasSetRounding flag, could skip lookup.
|
||||
regs_.Map(inst);
|
||||
fp_.FMOV(S0, regs_.F(inst.src1));
|
||||
|
||||
MOVP2R(SCRATCH1_64, ¤tRoundingFunc_);
|
||||
LDR(INDEX_UNSIGNED, SCRATCH1_64, SCRATCH1_64, 0);
|
||||
BLR(SCRATCH1_64);
|
||||
|
||||
fp_.FMOV(regs_.F(inst.dest), S0);
|
||||
break;
|
||||
|
||||
case IROp::FCvtSW:
|
||||
@@ -383,8 +391,40 @@ void Arm64JitBackend::CompIR_FCvt(IRInst inst) {
|
||||
break;
|
||||
|
||||
case IROp::FCvtScaledWS:
|
||||
if (IRRoundMode(inst.src2 >> 6) == IRRoundMode::CAST_1) {
|
||||
regs_.Map(inst);
|
||||
// NAN would convert to zero, so detect it specifically and replace with 0x7FFFFFFF.
|
||||
fp_.MVNI(32, EncodeRegToDouble(SCRATCHF2), 0x80, 24);
|
||||
fp_.FCMP(regs_.F(inst.src1), regs_.F(inst.src1));
|
||||
fp_.FCVTZS(regs_.F(inst.dest), regs_.F(inst.src1), inst.src2 & 0x1F);
|
||||
fp_.FCSEL(regs_.F(inst.dest), regs_.F(inst.dest), SCRATCHF2, CC_VC);
|
||||
} else {
|
||||
RoundingMode rm;
|
||||
switch (IRRoundMode(inst.src2 >> 6)) {
|
||||
case IRRoundMode::RINT_0: rm = RoundingMode::ROUND_N; break;
|
||||
case IRRoundMode::CEIL_2: rm = RoundingMode::ROUND_P; break;
|
||||
case IRRoundMode::FLOOR_3: rm = RoundingMode::ROUND_M; break;
|
||||
default:
|
||||
_assert_msg_(false, "Invalid rounding mode for FCvtScaledWS");
|
||||
}
|
||||
|
||||
// Unfortunately, only Z has a direct scaled instruction.
|
||||
// We'll have to multiply.
|
||||
regs_.Map(inst);
|
||||
fp_.MOVI2F(SCRATCHF1, (float)(1UL << (inst.src2 & 0x1F)), SCRATCH1);
|
||||
// This is for the NAN result.
|
||||
fp_.MVNI(32, EncodeRegToDouble(SCRATCHF2), 0x80, 24);
|
||||
fp_.FCMP(regs_.F(inst.src1), regs_.F(inst.src1));
|
||||
fp_.FMUL(regs_.F(inst.dest), regs_.F(inst.src1), SCRATCHF1);
|
||||
fp_.FCVTS(regs_.F(inst.dest), regs_.F(inst.dest), rm);
|
||||
fp_.FCSEL(regs_.F(inst.dest), regs_.F(inst.dest), SCRATCHF2, CC_VC);
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::FCvtScaledSW:
|
||||
CompIR_Generic(inst);
|
||||
// TODO: This is probably proceeded by a GPR transfer, might be ideal to combine.
|
||||
regs_.Map(inst);
|
||||
fp_.SCVTF(regs_.F(inst.dest), regs_.F(inst.src1), inst.src2 & 0x1F);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -433,8 +473,22 @@ void Arm64JitBackend::CompIR_FSat(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::FSat0_1:
|
||||
regs_.Map(inst);
|
||||
fp_.MOVI2F(SCRATCHF1, 1.0f);
|
||||
// Note that FMAX takes the larger of the two zeros, which is what we want.
|
||||
fp_.MOVI2F(SCRATCHF2, 0.0f);
|
||||
|
||||
fp_.FMIN(regs_.F(inst.dest), regs_.F(inst.src1), SCRATCHF1);
|
||||
fp_.FMAX(regs_.F(inst.dest), regs_.F(inst.dest), SCRATCHF2);
|
||||
break;
|
||||
|
||||
case IROp::FSatMinus1_1:
|
||||
CompIR_Generic(inst);
|
||||
regs_.Map(inst);
|
||||
fp_.MOVI2F(SCRATCHF1, 1.0f);
|
||||
fp_.FNEG(SCRATCHF2, SCRATCHF1);
|
||||
|
||||
fp_.FMIN(regs_.F(inst.dest), regs_.F(inst.src1), SCRATCHF1);
|
||||
fp_.FMAX(regs_.F(inst.dest), regs_.F(inst.dest), SCRATCHF2);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "Core/HLE/HLE.h"
|
||||
#include "Core/HLE/ReplaceTables.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/MIPS/IR/IRInterpreter.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRJit.h"
|
||||
#include "Core/MIPS/ARM64/Arm64IRRegCache.h"
|
||||
|
||||
@@ -84,14 +85,37 @@ void Arm64JitBackend::CompIR_Breakpoint(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Breakpoint:
|
||||
case IROp::MemoryCheck:
|
||||
CompIR_Generic(inst);
|
||||
FlushAll();
|
||||
// Note: the constant could be a delay slot.
|
||||
MOVI2R(W0, inst.constant);
|
||||
QuickCallFunction(SCRATCH2_64, &IRRunBreakpoint);
|
||||
break;
|
||||
|
||||
case IROp::MemoryCheck:
|
||||
{
|
||||
ARM64Reg addrBase = regs_.MapGPR(inst.src1);
|
||||
FlushAll();
|
||||
ADDI2R(W1, addrBase, inst.constant, SCRATCH1);
|
||||
MovFromPC(W0);
|
||||
ADDI2R(W0, W0, inst.dest, SCRATCH1);
|
||||
QuickCallFunction(SCRATCH2_64, &IRRunMemCheck);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
}
|
||||
|
||||
// Both return a flag on whether to bail out.
|
||||
ptrdiff_t distance = dispatcherCheckCoreState_ - GetCodePointer();
|
||||
if (distance >= -0x100000 && distance < 0x100000) {
|
||||
CBNZ(W0, dispatcherCheckCoreState_);
|
||||
} else {
|
||||
FixupBranch keepOnKeepingOn = CBZ(W0);
|
||||
B(dispatcherCheckCoreState_);
|
||||
SetJumpTarget(keepOnKeepingOn);
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JitBackend::CompIR_System(IRInst inst) {
|
||||
|
||||
@@ -581,8 +581,15 @@ void Arm64JitBackend::CompIR_VecClamp(IRInst inst) {
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Vec4ClampToZero:
|
||||
regs_.Map(inst);
|
||||
fp_.MOVI(32, EncodeRegToQuad(SCRATCHF1), 0);
|
||||
fp_.SMAX(32, regs_.FQ(inst.dest), regs_.FQ(inst.src1), EncodeRegToQuad(SCRATCHF1));
|
||||
break;
|
||||
|
||||
case IROp::Vec2ClampToZero:
|
||||
CompIR_Generic(inst);
|
||||
regs_.Map(inst);
|
||||
fp_.MOVI(32, EncodeRegToDouble(SCRATCHF1), 0);
|
||||
fp_.SMAX(32, regs_.FD(inst.dest), regs_.FD(inst.src1), EncodeRegToDouble(SCRATCHF1));
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -624,14 +631,33 @@ void Arm64JitBackend::CompIR_VecPack(IRInst inst) {
|
||||
CONDITIONAL_DISABLE;
|
||||
|
||||
switch (inst.op) {
|
||||
case IROp::Vec2Unpack16To31:
|
||||
case IROp::Vec4Pack32To8:
|
||||
case IROp::Vec2Pack31To16:
|
||||
case IROp::Vec4Unpack8To32:
|
||||
case IROp::Vec2Unpack16To32:
|
||||
case IROp::Vec4DuplicateUpperBitsAndShift1:
|
||||
case IROp::Vec4Pack31To8:
|
||||
CompIR_Generic(inst);
|
||||
// This operation swizzles the high 8 bits and converts to a signed int.
|
||||
// It's always after Vec4Unpack8To32.
|
||||
// 000A000B000C000D -> AAAABBBBCCCCDDDD and then shift right one (to match INT_MAX.)
|
||||
regs_.Map(inst);
|
||||
// First, USHR+ORR to get 0A0A0B0B0C0C0D0D.
|
||||
fp_.USHR(32, EncodeRegToQuad(SCRATCHF1), regs_.FQ(inst.src1), 16);
|
||||
fp_.ORR(EncodeRegToQuad(SCRATCHF1), EncodeRegToQuad(SCRATCHF1), regs_.FQ(inst.src1));
|
||||
// Now again, but by 8.
|
||||
fp_.USHR(32, regs_.FQ(inst.dest), EncodeRegToQuad(SCRATCHF1), 8);
|
||||
fp_.ORR(regs_.FQ(inst.dest), regs_.FQ(inst.dest), EncodeRegToQuad(SCRATCHF1));
|
||||
// Finally, shift away the sign. The goal is to saturate 0xFF -> 0x7FFFFFFF.
|
||||
fp_.USHR(32, regs_.FQ(inst.dest), regs_.FQ(inst.dest), 1);
|
||||
break;
|
||||
|
||||
case IROp::Vec2Pack31To16:
|
||||
// Same as Vec2Pack32To16, but we shift left 1 first to nuke the sign bit.
|
||||
if (Overlap(inst.dest, 1, inst.src1, 2)) {
|
||||
regs_.MapVec2(inst.src1, MIPSMap::DIRTY);
|
||||
fp_.SHL(32, EncodeRegToDouble(SCRATCHF1), regs_.FD(inst.src1), 1);
|
||||
fp_.UZP2(16, EncodeRegToDouble(SCRATCHF1), EncodeRegToDouble(SCRATCHF1), EncodeRegToDouble(SCRATCHF1));
|
||||
fp_.INS(32, regs_.FD(inst.dest & ~1), inst.dest & 1, EncodeRegToDouble(SCRATCHF1), 0);
|
||||
} else {
|
||||
regs_.Map(inst);
|
||||
fp_.SHL(32, regs_.FD(inst.dest), regs_.FD(inst.src1), 1);
|
||||
fp_.UZP2(16, regs_.FD(inst.dest), regs_.FD(inst.dest), regs_.FD(inst.dest));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Vec2Pack32To16:
|
||||
@@ -639,13 +665,102 @@ void Arm64JitBackend::CompIR_VecPack(IRInst inst) {
|
||||
if (Overlap(inst.dest, 1, inst.src1, 2)) {
|
||||
regs_.MapVec2(inst.src1, MIPSMap::DIRTY);
|
||||
fp_.UZP2(16, EncodeRegToDouble(SCRATCHF1), regs_.FD(inst.src1), regs_.FD(inst.src1));
|
||||
fp_.INS(32, regs_.FD(inst.dest), inst.dest & 1, EncodeRegToDouble(SCRATCHF1), 0);
|
||||
fp_.INS(32, regs_.FD(inst.dest & ~1), inst.dest & 1, EncodeRegToDouble(SCRATCHF1), 0);
|
||||
} else {
|
||||
regs_.Map(inst);
|
||||
fp_.UZP2(16, regs_.FD(inst.dest), regs_.FD(inst.src1), regs_.FD(inst.src1));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Vec4Pack31To8:
|
||||
if (Overlap(inst.dest, 1, inst.src1, 4)) {
|
||||
regs_.MapVec4(inst.src1, MIPSMap::DIRTY);
|
||||
} else {
|
||||
regs_.Map(inst);
|
||||
}
|
||||
|
||||
// Viewed as 8-bit lanes, after a shift by 23: AxxxBxxxCxxxDxxx.
|
||||
// So: UZP1 -> AxBxCxDx -> UZP1 again -> ABCD
|
||||
fp_.USHR(32, EncodeRegToQuad(SCRATCHF1), regs_.FQ(inst.src1), 23);
|
||||
fp_.UZP1(8, EncodeRegToQuad(SCRATCHF1), EncodeRegToQuad(SCRATCHF1), EncodeRegToQuad(SCRATCHF1));
|
||||
// Second one directly to dest, if we can.
|
||||
if (Overlap(inst.dest, 1, inst.src1, 4)) {
|
||||
fp_.UZP1(8, EncodeRegToQuad(SCRATCHF1), EncodeRegToQuad(SCRATCHF1), EncodeRegToQuad(SCRATCHF1));
|
||||
fp_.INS(32, regs_.FQ(inst.dest & ~3), inst.dest & 3, EncodeRegToQuad(SCRATCHF1), 0);
|
||||
} else {
|
||||
fp_.UZP1(8, regs_.FQ(inst.dest), EncodeRegToQuad(SCRATCHF1), EncodeRegToQuad(SCRATCHF1));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Vec4Pack32To8:
|
||||
if (Overlap(inst.dest, 1, inst.src1, 4)) {
|
||||
regs_.MapVec4(inst.src1, MIPSMap::DIRTY);
|
||||
} else {
|
||||
regs_.Map(inst);
|
||||
}
|
||||
|
||||
// Viewed as 8-bit lanes, after a shift by 24: AxxxBxxxCxxxDxxx.
|
||||
// Same as Vec4Pack31To8, just a different shift.
|
||||
fp_.USHR(32, EncodeRegToQuad(SCRATCHF1), regs_.FQ(inst.src1), 24);
|
||||
fp_.UZP1(8, EncodeRegToQuad(SCRATCHF1), EncodeRegToQuad(SCRATCHF1), EncodeRegToQuad(SCRATCHF1));
|
||||
// Second one directly to dest, if we can.
|
||||
if (Overlap(inst.dest, 1, inst.src1, 4)) {
|
||||
fp_.UZP1(8, EncodeRegToQuad(SCRATCHF1), EncodeRegToQuad(SCRATCHF1), EncodeRegToQuad(SCRATCHF1));
|
||||
fp_.INS(32, regs_.FQ(inst.dest & ~3), inst.dest & 3, EncodeRegToQuad(SCRATCHF1), 0);
|
||||
} else {
|
||||
fp_.UZP1(8, regs_.FQ(inst.dest), EncodeRegToQuad(SCRATCHF1), EncodeRegToQuad(SCRATCHF1));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Vec2Unpack16To31:
|
||||
// Viewed as 16-bit: ABxx -> 0A0B, then shift a zero into the sign place.
|
||||
if (Overlap(inst.dest, 2, inst.src1, 1)) {
|
||||
regs_.MapVec2(inst.dest, MIPSMap::DIRTY);
|
||||
} else {
|
||||
regs_.Map(inst);
|
||||
}
|
||||
if (inst.src1 == inst.dest + 1) {
|
||||
fp_.USHLL2(16, regs_.FQ(inst.dest), regs_.FD(inst.src1), 15);
|
||||
} else {
|
||||
fp_.USHLL(16, regs_.FQ(inst.dest), regs_.FD(inst.src1), 15);
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Vec2Unpack16To32:
|
||||
// Just Vec2Unpack16To31, without the shift.
|
||||
if (Overlap(inst.dest, 2, inst.src1, 1)) {
|
||||
regs_.MapVec2(inst.dest, MIPSMap::DIRTY);
|
||||
} else {
|
||||
regs_.Map(inst);
|
||||
}
|
||||
if (inst.src1 == inst.dest + 1) {
|
||||
fp_.SHLL2(16, regs_.FQ(inst.dest), regs_.FD(inst.src1));
|
||||
} else {
|
||||
fp_.SHLL(16, regs_.FQ(inst.dest), regs_.FD(inst.src1));
|
||||
}
|
||||
break;
|
||||
|
||||
case IROp::Vec4Unpack8To32:
|
||||
// Viewed as 8-bit: ABCD -> 000A000B000C000D.
|
||||
if (Overlap(inst.dest, 4, inst.src1, 1)) {
|
||||
regs_.MapVec4(inst.dest, MIPSMap::DIRTY);
|
||||
if (inst.dest == inst.src1 + 2) {
|
||||
fp_.SHLL2(8, regs_.FQ(inst.dest), regs_.FD(inst.src1 & ~3));
|
||||
} else if (inst.dest != inst.src1) {
|
||||
fp_.DUP(32, regs_.FQ(inst.dest), regs_.FQ(inst.src1), inst.src1 & 3);
|
||||
fp_.SHLL(8, regs_.FQ(inst.dest), regs_.FD(inst.dest));
|
||||
} else {
|
||||
fp_.SHLL(8, regs_.FQ(inst.dest), regs_.FD(inst.src1));
|
||||
}
|
||||
fp_.SHLL(16, regs_.FQ(inst.dest), regs_.FD(inst.dest));
|
||||
} else {
|
||||
regs_.Map(inst);
|
||||
// Two steps: ABCD -> 0A0B0C0D, then to 000A000B000C000D.
|
||||
fp_.SHLL(8, regs_.FQ(inst.dest), regs_.FD(inst.src1));
|
||||
fp_.SHLL(16, regs_.FQ(inst.dest), regs_.FD(inst.dest));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
INVALIDOP;
|
||||
break;
|
||||
|
||||
@@ -926,10 +926,8 @@ namespace MIPSInt
|
||||
switch ((op >> 16) & 3) {
|
||||
case 0: // vuc2i
|
||||
// Quad is the only option.
|
||||
// This operation is weird. This particular way of working matches hw but does not
|
||||
// seem quite sane.
|
||||
// I guess it's used for fixed-point math, and fills more bits to facilitate
|
||||
// conversion between 8-bit and 16-bit values. But then why not do it in vc2i?
|
||||
// This converts 8-bit unsigned to 31-bit signed, swizzling to saturate.
|
||||
// Similar to 5-bit to 8-bit color swizzling, but clamping to INT_MAX.
|
||||
{
|
||||
u32 value = s[0];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
@@ -942,6 +940,8 @@ namespace MIPSInt
|
||||
|
||||
case 1: // vc2i
|
||||
// Quad is the only option
|
||||
// Unlike vuc2i, the source and destination are signed so there is no shift.
|
||||
// It lacks the swizzle because of negative values.
|
||||
{
|
||||
u32 value = s[0];
|
||||
d[0] = (value & 0xFF) << 24;
|
||||
@@ -953,6 +953,7 @@ namespace MIPSInt
|
||||
break;
|
||||
|
||||
case 2: // vus2i
|
||||
// Note: for some reason, this skips swizzle such that 0xFFFF -> 0x7FFF8000 unlike vuc2i.
|
||||
oz = V_Pair;
|
||||
switch (sz) {
|
||||
case V_Quad:
|
||||
|
||||
@@ -233,30 +233,29 @@ void RiscVJitBackend::CompIR_FCvt(IRInst inst) {
|
||||
break;
|
||||
|
||||
case IROp::FCvtScaledWS:
|
||||
if (cpu_info.RiscV_D) {
|
||||
Round rm = Round::NEAREST_EVEN;
|
||||
switch (inst.src2 >> 6) {
|
||||
case 0: rm = Round::NEAREST_EVEN; break;
|
||||
case 1: rm = Round::TOZERO; break;
|
||||
case 2: rm = Round::UP; break;
|
||||
case 3: rm = Round::DOWN; break;
|
||||
}
|
||||
|
||||
tempReg = regs_.MapWithFPRTemp(inst);
|
||||
// Prepare the double src1 and the multiplier.
|
||||
FCVT(FConv::D, FConv::S, regs_.F(inst.dest), regs_.F(inst.src1));
|
||||
LI(SCRATCH1, 1UL << (inst.src2 & 0x1F));
|
||||
FCVT(FConv::D, FConv::WU, tempReg, SCRATCH1, 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, regs_.F(inst.dest), rm);
|
||||
// TODO: Could combine with a transfer, often is one...
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1);
|
||||
} else {
|
||||
CompIR_Generic(inst);
|
||||
{
|
||||
Round rm = Round::NEAREST_EVEN;
|
||||
switch (inst.src2 >> 6) {
|
||||
case 0: rm = Round::NEAREST_EVEN; break;
|
||||
case 1: rm = Round::TOZERO; break;
|
||||
case 2: rm = Round::UP; break;
|
||||
case 3: rm = Round::DOWN; break;
|
||||
default:
|
||||
_assert_msg_(false, "Invalid rounding mode for FCvtScaledWS");
|
||||
}
|
||||
|
||||
tempReg = regs_.MapWithFPRTemp(inst);
|
||||
// Prepare the multiplier.
|
||||
LI(SCRATCH1, 1UL << (inst.src2 & 0x1F));
|
||||
FCVT(FConv::S, FConv::WU, tempReg, SCRATCH1, rm);
|
||||
|
||||
FMUL(32, regs_.F(inst.dest), regs_.F(inst.src1), tempReg, rm);
|
||||
// NAN and clamping should all be correct.
|
||||
FCVT(FConv::W, FConv::S, SCRATCH1, regs_.F(inst.dest), rm);
|
||||
// TODO: Could combine with a transfer, often is one...
|
||||
FMV(FMv::W, FMv::X, regs_.F(inst.dest), SCRATCH1);
|
||||
break;
|
||||
}
|
||||
|
||||
case IROp::FCvtScaledSW:
|
||||
// TODO: This is probably proceeded by a GPR transfer, might be ideal to combine.
|
||||
|
||||
Reference in new issue
Block a user