Merge pull request #18103 from unknownbrackets/arm64jit-more

Implement the remaining arm64jit IR ops
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2023-09-08 09:59:47 +02:00
commit 7b9e03ef4a
7 files changed
+325 -50

No files matched your search

+84 -10
View File
@@ -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__);
+8
View File
@@ -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);
+57 -3
View File
@@ -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, &currentRoundingFunc_);
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:
+26 -2
View File
@@ -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) {
+124 -9
View File
@@ -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;
+5 -4
View File
@@ -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:
+21 -22
View File
@@ -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.