diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index caaade1540..c944f4019c 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1503,6 +1503,35 @@ namespace MIPSComp { ApplyPrefixD(dregs, outsize, _VD); } + void IRFrontend::Comp_Vf2h(MIPSOpcode op) { + CONDITIONAL_DISABLE(VFPU_VEC); + // The prefixes act oddly here (the S swizzle applies to all four lanes), so leave those to the interpreter. + if (!js.HasNoPrefix()) { + DISABLE; + } + + // Vector pack float to half + // d[N] = half(s[N*2]) | half(s[N*2+1]) << 16 + VectorSize sz = GetVecSize(op); + if (sz != V_Pair && sz != V_Quad) { + DISABLE; + } + VectorSize outsize = sz == V_Pair ? V_Single : V_Pair; + int nOut = GetNumVectorElements(outsize); + + u8 sregs[4], dregs[2]; + GetVectorRegs(sregs, sz, _VS); + GetVectorRegs(dregs, outsize, _VD); + + // Through temps, since d may overlap s. + for (int i = 0; i < nOut; i++) { + ir.Write(IROp::FFloatToHalf2, IRVTEMP_0 + i, sregs[i * 2], sregs[i * 2 + 1]); + } + for (int i = 0; i < nOut; i++) { + ir.Write(IROp::FMov, dregs[i], IRVTEMP_0 + i); + } + } + void IRFrontend::Comp_Vf2i(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || (js.prefixD & 0xFF) != 0) { @@ -2743,6 +2772,60 @@ namespace MIPSComp { ApplyPrefixD(dregs, outsize, _VD); } + void IRFrontend::Comp_Vsrt(MIPSOpcode op) { + CONDITIONAL_DISABLE(VFPU_VEC); + // T is S re-swizzled, with the T prefix rewritten around that, so S and T prefixes go to the interpreter. + // The D prefix applies normally (Colin McRae masks off half the lanes). + if (js.HasUnknownPrefix() || js.HasSPrefix() || js.HasTPrefix() || GetVecSize(op) != V_Quad) { + DISABLE; + } + + // Sorts pairs of lanes with vmin/vmax, which keep the second operand on a tie, in the + // same operand order as Int_Vsrt1-4, so the same lane wins a tie. + // vsrt1: (x, y) (z, w) ascending. vsrt2: (x, w) (y, z) ascending. + // vsrt3: (x, y) (z, w) descending. vsrt4: (x, w) (y, z) descending. + u8 s[4], dregs[4]; + GetVectorRegs(s, V_Quad, _VS); + GetVectorRegsPrefixD(dregs, V_Quad, _VD); + + u8 tmp[4]; + for (int i = 0; i < 4; i++) { + tmp[i] = IRVTEMP_0 + i; + } + switch ((op >> 16) & 0x1F) { + case 0: // vsrt1, t = yxwz + ir.Write(IROp::FMin, tmp[0], s[1], s[0]); + ir.Write(IROp::FMax, tmp[1], s[1], s[0]); + ir.Write(IROp::FMin, tmp[2], s[3], s[2]); + ir.Write(IROp::FMax, tmp[3], s[3], s[2]); + break; + case 1: // vsrt2, t = wzyx + ir.Write(IROp::FMin, tmp[0], s[3], s[0]); + ir.Write(IROp::FMin, tmp[1], s[2], s[1]); + ir.Write(IROp::FMax, tmp[2], s[2], s[1]); + ir.Write(IROp::FMax, tmp[3], s[3], s[0]); + break; + case 8: // vsrt3, t = yxwz + ir.Write(IROp::FMax, tmp[0], s[0], s[1]); + ir.Write(IROp::FMin, tmp[1], s[0], s[1]); + ir.Write(IROp::FMax, tmp[2], s[2], s[3]); + ir.Write(IROp::FMin, tmp[3], s[2], s[3]); + break; + case 9: // vsrt4, t = wzyx + ir.Write(IROp::FMax, tmp[0], s[0], s[3]); + ir.Write(IROp::FMax, tmp[1], s[1], s[2]); + ir.Write(IROp::FMin, tmp[2], s[1], s[2]); + ir.Write(IROp::FMin, tmp[3], s[0], s[3]); + break; + default: + DISABLE; + } + for (int i = 0; i < 4; i++) { + ir.Write(IROp::FMov, dregs[i], tmp[i]); + } + ApplyPrefixD(dregs, V_Quad, _VD); + } + void IRFrontend::Comp_Vbfy(MIPSOpcode op) { CONDITIONAL_DISABLE(VFPU_VEC); if (js.HasUnknownPrefix() || !IsPrefixWithinSize(js.prefixS, op) || js.HasTPrefix() || (js.prefixS & VFPU_NEGATE(1, 1, 1, 1)) != 0) { diff --git a/Core/MIPS/IR/IRFrontend.h b/Core/MIPS/IR/IRFrontend.h index 12e98a28be..a363900c8b 100644 --- a/Core/MIPS/IR/IRFrontend.h +++ b/Core/MIPS/IR/IRFrontend.h @@ -92,6 +92,8 @@ public: void Comp_Vocp(MIPSOpcode op) override; void Comp_ColorConv(MIPSOpcode op) override; void Comp_Vbfy(MIPSOpcode op) override; + void Comp_Vsrt(MIPSOpcode op) override; + void Comp_Vf2h(MIPSOpcode op) override; int Replace_fabsf() override; // Returns true if a loaded state means blocks compiled before it can't run any more. diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index 8cd54e8985..bd0fa47c33 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -122,6 +122,7 @@ static const IRMeta irMeta[] = { { IROp::FLog2, "FLog2", "FF" }, { IROp::FHalfToFloat, "FHalfToFloat", "FFI" }, { IROp::FSinCos, "FSinCos", "2F" }, + { IROp::FFloatToHalf2, "FFloatToHalf2", "FFF" }, { IROp::FNeg, "FNeg", "FF" }, { IROp::FSign, "FSign", "FF" }, { IROp::FAbs, "FAbs", "FF" }, diff --git a/Core/MIPS/IR/IRInst.h b/Core/MIPS/IR/IRInst.h index ab8b5c38d3..47d74d63f4 100644 --- a/Core/MIPS/IR/IRInst.h +++ b/Core/MIPS/IR/IRInst.h @@ -231,6 +231,7 @@ enum class IROp : uint8_t { FLog2, // vlog2, bit-exact with the PSP FHalfToFloat, // vh2f of the lower (src2 = 0) or upper (src2 = 1) half of src1 FSinCos, // dest = sin(src1), dest + 1 = cos(src1), from one argument reduction + FFloatToHalf2, // vf2h of a pair: dest = half(src1) | half(src2) << 16, bit-exact with the PSP // Fake/System instructions Interpret, diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index b399fe4a2a..cd9d5c7871 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -272,6 +272,7 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, const IRInst *chainArena) { dispatch[(int)IROp::FExp2] = &&L_FExp2; dispatch[(int)IROp::FLog2] = &&L_FLog2; dispatch[(int)IROp::FSinCos] = &&L_FSinCos; + dispatch[(int)IROp::FFloatToHalf2] = &&L_FFloatToHalf2; dispatch[(int)IROp::FHalfToFloat] = &&L_FHalfToFloat; dispatch[(int)IROp::ShlImm] = &&L_ShlImm; dispatch[(int)IROp::ShrImm] = &&L_ShrImm; @@ -992,6 +993,9 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, const IRInst *chainArena) { mips->f[inst->dest + 1] = c; IR_NEXT; } + IR_CASE(FFloatToHalf2) + mips->fi[inst->dest] = vfpu_f2h(mips->fi[inst->src1]) | ((u32)vfpu_f2h(mips->fi[inst->src2]) << 16); + IR_NEXT; IR_CASE(FHalfToFloat) mips->fi[inst->dest] = vfpu_h2f((u16)(inst->src2 ? mips->fi[inst->src1] >> 16 : mips->fi[inst->src1] & 0xFFFF)); IR_NEXT; diff --git a/Core/MIPS/IR/IRNativeCommon.cpp b/Core/MIPS/IR/IRNativeCommon.cpp index b24d5a5b84..7e9199c873 100644 --- a/Core/MIPS/IR/IRNativeCommon.cpp +++ b/Core/MIPS/IR/IRNativeCommon.cpp @@ -459,6 +459,11 @@ void IRNativeBackend::CompileIRInst(IRInst inst) { CompIR_FSpecial(inst); break; + case IROp::FFloatToHalf2: + // Rare enough that the backends just call the IR interpreter's version. + CompIR_Generic(inst); + break; + case IROp::Interpret: CompIR_Interpret(inst); break; diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index bc8c5a19b7..529a6c0140 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -809,6 +809,7 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts case IROp::FLog2: case IROp::FHalfToFloat: case IROp::FSinCos: + case IROp::FFloatToHalf2: case IROp::FMin: case IROp::FMax: case IROp::FSign: diff --git a/Core/MIPS/JitCommon/JitCommon.h b/Core/MIPS/JitCommon/JitCommon.h index aba216c6e7..7bee89689c 100644 --- a/Core/MIPS/JitCommon/JitCommon.h +++ b/Core/MIPS/JitCommon/JitCommon.h @@ -109,6 +109,8 @@ namespace MIPSComp { virtual void Comp_Vocp(MIPSOpcode op) = 0; virtual void Comp_ColorConv(MIPSOpcode op) = 0; virtual void Comp_Vbfy(MIPSOpcode op) = 0; + virtual void Comp_Vsrt(MIPSOpcode op) = 0; + virtual void Comp_Vf2h(MIPSOpcode op) = 0; virtual void Comp_DoNothing(MIPSOpcode op) = 0; virtual int Replace_fabsf() = 0; diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index 55604cefda..bea541fe52 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -613,7 +613,7 @@ static const MIPSInstruction tableVFPU7[32] = // 110100 00001 xxxxx . ....... . //16 INVALID, INVALID, - INSTR("vf2h", JITFUNC(Comp_Generic), Dis_Vf2h, Int_Vf2h, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), + INSTR("vf2h", JITFUNC(Comp_Vf2h), Dis_Vf2h, Int_Vf2h, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), INSTR("vh2f", JITFUNC(Comp_Vh2f), Dis_Vh2f, Int_Vh2f, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), INVALID, @@ -738,8 +738,8 @@ static const MIPSInstruction tableVFPUMatrixSet1[16] = // 111100 11100 .xxxx . . static const MIPSInstruction tableVFPU9[32] = // 110100 00010 xxxxx . ....... . ....... { - INSTR("vsrt1", JITFUNC(Comp_Generic), Dis_Vbfy, Int_Vsrt1, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), - INSTR("vsrt2", JITFUNC(Comp_Generic), Dis_Vbfy, Int_Vsrt2, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), + INSTR("vsrt1", JITFUNC(Comp_Vsrt), Dis_Vbfy, Int_Vsrt1, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), + INSTR("vsrt2", JITFUNC(Comp_Vsrt), Dis_Vbfy, Int_Vsrt2, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), INSTR("vbfy1", JITFUNC(Comp_Vbfy), Dis_Vbfy, Int_Vbfy, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), INSTR("vbfy2", JITFUNC(Comp_Vbfy), Dis_Vbfy, Int_Vbfy, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), //4 @@ -748,8 +748,8 @@ static const MIPSInstruction tableVFPU9[32] = // 110100 00010 xxxxx . ....... . INSTR("vfad", JITFUNC(Comp_Vhoriz), Dis_Vfad, Int_Vfad, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), INSTR("vavg", JITFUNC(Comp_Vhoriz), Dis_Vfad, Int_Vavg, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), //8 - INSTR("vsrt3", JITFUNC(Comp_Generic), Dis_Vbfy, Int_Vsrt3, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), - INSTR("vsrt4", JITFUNC(Comp_Generic), Dis_Vbfy, Int_Vsrt4, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), + INSTR("vsrt3", JITFUNC(Comp_Vsrt), Dis_Vbfy, Int_Vsrt3, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), + INSTR("vsrt4", JITFUNC(Comp_Vsrt), Dis_Vbfy, Int_Vsrt4, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), INSTR("vsgn", JITFUNC(Comp_Vsgn), Dis_Vbfy, Int_Vsgn, IN_OTHER|OUT_OTHER|IS_VFPU|OUT_EAT_PREFIX), INVALID, //12