diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index 70128b4c62..1c767d8e3d 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -929,37 +929,17 @@ namespace MIPSComp { VectorSize sz = GetVecSize(op); int n = GetNumVectorElements(sz); - int imm = (op >> 16) & 0x1f; - const float mult = 1.0f / (float)(1UL << imm); + uint8_t imm = (op >> 16) & 0x1f; u8 sregs[4], dregs[4]; GetVectorRegsPrefixS(sregs, sz, _VS); GetVectorRegsPrefixD(dregs, sz, _VD); - u8 tempregs[4]; - for (int i = 0; i < n; ++i) { - if (!IsOverlapSafe(dregs[i], n, sregs)) { - tempregs[i] = IRVTEMP_PFX_T + i; // Need IRVTEMP_0 for the scaling factor - } else { - tempregs[i] = dregs[i]; - } - } - if (mult != 1.0f) - ir.Write(IROp::SetConstF, IRVTEMP_0, ir.AddConstantFloat(mult)); - // TODO: Use the SCVTF with builtin scaling where possible. for (int i = 0; i < n; i++) { - ir.Write(IROp::FCvtSW, tempregs[i], sregs[i]); - } - if (mult != 1.0f) { - for (int i = 0; i < n; i++) { - ir.Write(IROp::FMul, tempregs[i], tempregs[i], IRVTEMP_0); - } - } - - for (int i = 0; i < n; ++i) { - if (dregs[i] != tempregs[i]) { - ir.Write(IROp::FMov, dregs[i], tempregs[i]); - } + if (imm == 0) + ir.Write(IROp::FCvtSW, dregs[i], sregs[i]); + else + ir.Write(IROp::FCvtScaledSW, dregs[i], sregs[i], imm); } ApplyPrefixD(dregs, sz); } @@ -986,7 +966,47 @@ namespace MIPSComp { // d[N] = int(S[N] * mult) // Note: saturates on overflow. - DISABLE; + VectorSize sz = GetVecSize(op); + int n = GetNumVectorElements(sz); + + uint8_t imm = (op >> 16) & 0x1f; + + u8 sregs[4], dregs[4]; + GetVectorRegsPrefixS(sregs, sz, _VS); + GetVectorRegsPrefixD(dregs, sz, _VD); + + // Same values as FCR31. + uint8_t rmode = (op >> 21) & 3; + if (((op >> 21) & 0x1C) != 0x10) + INVALIDOP; + + if (imm != 0) { + for (int i = 0; i < n; i++) + ir.Write(IROp::FCvtScaledWS, dregs[i], sregs[i], imm | (rmode << 6)); + } else { + for (int i = 0; i < n; i++) { + switch (rmode) { + case 0: // vf2in + ir.Write(IROp::FRound, dregs[i], sregs[i]); + break; + + case 1: // vf2iz + ir.Write(IROp::FTrunc, dregs[i], sregs[i]); + break; + + case 2: // vf2iu + ir.Write(IROp::FCeil, dregs[i], sregs[i]); + break; + + case 3: // vf2id + ir.Write(IROp::FFloor, dregs[i], sregs[i]); + break; + + default: + INVALIDOP; + } + } + } } void IRFrontend::Comp_Mftv(MIPSOpcode op) { diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index 116908c656..6d7d46e888 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -105,6 +105,8 @@ static const IRMeta irMeta[] = { { IROp::FFloor, "FFloor", "FF" }, { IROp::FCvtWS, "FCvtWS", "FF" }, { IROp::FCvtSW, "FCvtSW", "FF" }, + { IROp::FCvtScaledWS, "FCvtScaledWS", "FFI" }, + { IROp::FCvtScaledSW, "FCvtScaledSW", "FFI" }, { IROp::FCmp, "FCmp", "mFF" }, { IROp::FSat0_1, "FSat(0 - 1)", "FF" }, { IROp::FSatMinus1_1, "FSat(-1 - 1)", "FF" }, diff --git a/Core/MIPS/IR/IRInst.h b/Core/MIPS/IR/IRInst.h index d4eb47f49a..2ffc5c79f2 100644 --- a/Core/MIPS/IR/IRInst.h +++ b/Core/MIPS/IR/IRInst.h @@ -127,6 +127,8 @@ enum class IROp : u8 { FCvtWS, FCvtSW, + FCvtScaledWS, + FCvtScaledSW, FMovFromGPR, FMovToGPR, @@ -304,9 +306,6 @@ enum : IRReg { IRVTEMP_PFX_D = 232 - 32, IRVTEMP_0 = 236 - 32, - // 16 float temps for vector S and T prefixes and things like that. - // IRVTEMP_0 = 208 - 64, // -64 to be relative to v[0] - // Hacky way to get to other state IRREG_VFPU_CTRL_BASE = 208, IRREG_VFPU_CC = 211, diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index 561adf6f25..c15d61f518 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -834,7 +834,7 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { mips->fi[inst->dest] = my_isinf(value) && value < 0.0f ? -2147483648LL : 2147483647LL; break; } else { - mips->fs[inst->dest] = (int)floorf(value + 0.5f); + mips->fs[inst->dest] = (int)round_ieee_754(value); } break; } @@ -931,6 +931,35 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { } break; //cvt.w.s } + case IROp::FCvtScaledSW: + mips->f[inst->dest] = (float)mips->fs[inst->src1] * (1.0f / (1UL << (inst->src2 & 0x1F))); + break; + case IROp::FCvtScaledWS: + { + float src = mips->f[inst->src1]; + if (my_isnan(src)) { + // TODO: True for negatives too? + mips->fs[inst->dest] = 2147483647L; + break; + } + + float mult = (float)(1UL << (inst->src2 & 0x1F)); + double sv = src * mult; // (float)0x7fffffff == (float)0x80000000 + // Cap/floor it to 0x7fffffff / 0x80000000 + if (sv > (double)0x7fffffff) { + mips->fs[inst->dest] = 0x7fffffff; + } else if (sv <= (double)(int)0x80000000) { + mips->fs[inst->dest] = 0x80000000; + } else { + switch (inst->src2 >> 6) { + case 0: mips->fs[inst->dest] = (int)round_ieee_754(sv); break; + case 1: mips->fs[inst->dest] = src >= 0 ? (int)floor(sv) : (int)ceil(sv); break; + case 2: mips->fs[inst->dest] = (int)ceil(sv); break; + case 3: mips->fs[inst->dest] = (int)floor(sv); break; + } + } + break; + } case IROp::ZeroFpCond: mips->fpcond = 0; diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index 52ea66c5d4..7f218d35c7 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -667,6 +667,8 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts case IROp::FCeil: case IROp::FFloor: case IROp::FCvtSW: + case IROp::FCvtScaledWS: + case IROp::FCvtScaledSW: case IROp::FSin: case IROp::FCos: case IROp::FSqrt: diff --git a/Core/MIPS/RiscV/RiscVCompFPU.cpp b/Core/MIPS/RiscV/RiscVCompFPU.cpp index 84c7faa832..fd3807d907 100644 --- a/Core/MIPS/RiscV/RiscVCompFPU.cpp +++ b/Core/MIPS/RiscV/RiscVCompFPU.cpp @@ -190,7 +190,9 @@ void RiscVJit::CompIR_FCvt(IRInst inst) { switch (inst.op) { case IROp::FCvtWS: + case IROp::FCvtScaledWS: case IROp::FCvtSW: + case IROp::FCvtScaledSW: CompIR_Generic(inst); break; diff --git a/Core/MIPS/RiscV/RiscVJit.cpp b/Core/MIPS/RiscV/RiscVJit.cpp index b94369c4ca..73962198c2 100644 --- a/Core/MIPS/RiscV/RiscVJit.cpp +++ b/Core/MIPS/RiscV/RiscVJit.cpp @@ -265,6 +265,8 @@ void RiscVJit::CompileIRInst(IRInst inst) { case IROp::FCvtWS: case IROp::FCvtSW: + case IROp::FCvtScaledWS: + case IROp::FCvtScaledSW: CompIR_FCvt(inst); break;