From c85886c11e4dae5ac4891ddd3f0e21d7929d0169 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 1 Sep 2023 22:29:24 -0700 Subject: [PATCH 1/2] irjit: Use enum for rounding modes. --- Core/MIPS/IR/IRCompVFPU.cpp | 10 +++++----- Core/MIPS/IR/IRInst.h | 7 +++++++ Core/MIPS/IR/IRInterpreter.cpp | 20 ++++++++++---------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/Core/MIPS/IR/IRCompVFPU.cpp b/Core/MIPS/IR/IRCompVFPU.cpp index fe1d9be969..e42ac78d25 100644 --- a/Core/MIPS/IR/IRCompVFPU.cpp +++ b/Core/MIPS/IR/IRCompVFPU.cpp @@ -1196,20 +1196,20 @@ namespace MIPSComp { ir.Write(IROp::FCvtScaledWS, dregs[i], sregs[i], imm | (rmode << 6)); } else { for (int i = 0; i < n; i++) { - switch (rmode) { - case 0: // vf2in + switch (IRRoundMode(rmode)) { + case IRRoundMode::RINT_0: // vf2in ir.Write(IROp::FRound, dregs[i], sregs[i]); break; - case 1: // vf2iz + case IRRoundMode::CAST_1: // vf2iz ir.Write(IROp::FTrunc, dregs[i], sregs[i]); break; - case 2: // vf2iu + case IRRoundMode::CEIL_2: // vf2iu ir.Write(IROp::FCeil, dregs[i], sregs[i]); break; - case 3: // vf2id + case IRRoundMode::FLOOR_3: // vf2id ir.Write(IROp::FFloor, dregs[i], sregs[i]); break; diff --git a/Core/MIPS/IR/IRInst.h b/Core/MIPS/IR/IRInst.h index f49ac464ad..970faa34c2 100644 --- a/Core/MIPS/IR/IRInst.h +++ b/Core/MIPS/IR/IRInst.h @@ -252,6 +252,13 @@ enum class Vec4Init { Set_0001, }; +enum class IRRoundMode : uint8_t { + RINT_0 = 0, + CAST_1 = 1, + CEIL_2 = 2, + FLOOR_3 = 3, +}; + // Hm, unused inline IRComparison Invert(IRComparison comp) { switch (comp) { diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index d3535fd16e..ade6d64b62 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -966,11 +966,11 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { mips->fs[inst->dest] = my_isinf(src) && src < 0.0f ? -2147483648LL : 2147483647LL; break; } - switch (mips->fcr31 & 3) { - case 0: mips->fs[inst->dest] = (int)round_ieee_754(src); break; // RINT_0 - case 1: mips->fs[inst->dest] = (int)src; break; // CAST_1 - case 2: mips->fs[inst->dest] = (int)ceilf(src); break; // CEIL_2 - case 3: mips->fs[inst->dest] = (int)floorf(src); break; // FLOOR_3 + switch (IRRoundMode(mips->fcr31 & 3)) { + case IRRoundMode::RINT_0: mips->fs[inst->dest] = (int)round_ieee_754(src); break; + case IRRoundMode::CAST_1: mips->fs[inst->dest] = (int)src; break; + case IRRoundMode::CEIL_2: mips->fs[inst->dest] = (int)ceilf(src); break; + case IRRoundMode::FLOOR_3: mips->fs[inst->dest] = (int)floorf(src); break; } break; //cvt.w.s } @@ -994,11 +994,11 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, int count) { } 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; + switch (IRRoundMode(inst->src2 >> 6)) { + case IRRoundMode::RINT_0: mips->fs[inst->dest] = (int)round_ieee_754(sv); break; + case IRRoundMode::CAST_1: mips->fs[inst->dest] = src >= 0 ? (int)floor(sv) : (int)ceil(sv); break; + case IRRoundMode::CEIL_2: mips->fs[inst->dest] = (int)ceil(sv); break; + case IRRoundMode::FLOOR_3: mips->fs[inst->dest] = (int)floor(sv); break; } } break; From c5d0e1d0cc9177ba627493808ab1b58067c1b1ba Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 1 Sep 2023 22:31:25 -0700 Subject: [PATCH 2/2] x86jit: Implement float to integer. --- Core/MIPS/x86/X64IRCompFPU.cpp | 118 ++++++++++++++++++++++++++++++++- Core/MIPS/x86/X64IRJit.h | 3 + Core/MIPS/x86/X64IRRegCache.h | 1 + 3 files changed, 120 insertions(+), 2 deletions(-) diff --git a/Core/MIPS/x86/X64IRCompFPU.cpp b/Core/MIPS/x86/X64IRCompFPU.cpp index 618e844b3d..15101eb209 100644 --- a/Core/MIPS/x86/X64IRCompFPU.cpp +++ b/Core/MIPS/x86/X64IRCompFPU.cpp @@ -56,6 +56,21 @@ void X64JitBackend::EmitFPUConstants() { Write32(val); } + + constants.mulTableVf2i = (const double *)GetCodePointer(); + for (uint8_t i = 0; i < 32; ++i) { + double fval = (1UL << i); + uint64_t val; + memcpy(&val, &fval, sizeof(val)); + + Write64(val); + } + + // Note: this first one is (double)(int)0x80000000, sign extended. + constants.minIntAsDouble = (const double *)GetCodePointer(); + Write64(0xC1E0000000000000ULL); + constants.maxIntAsDouble = (const double *)GetCodePointer(); + Write64(0x41DFFFFFFFC00000ULL); } void X64JitBackend::CompIR_FArith(IRInst inst) { @@ -533,8 +548,18 @@ void X64JitBackend::CompIR_FCvt(IRInst inst) { switch (inst.op) { case IROp::FCvtWS: - CompIR_Generic(inst); + { + regs_.Map(inst); + UCOMISS(regs_.FX(inst.src1), M(constants.positiveInfinity)); // rip accessible + + CVTPS2DQ(regs_.FX(inst.dest), regs_.F(inst.src1)); + // UCOMISS set ZF if EQUAL (to infinity) or UNORDERED. + FixupBranch skip = J_CC(CC_NZ); + MOVAPS(regs_.FX(inst.dest), M(constants.noSignMask)); // rip accessible + + SetJumpTarget(skip); break; + } case IROp::FCvtSW: regs_.Map(inst); @@ -542,7 +567,96 @@ void X64JitBackend::CompIR_FCvt(IRInst inst) { break; case IROp::FCvtScaledWS: - CompIR_Generic(inst); + regs_.Map(inst); + if (cpu_info.bSSE4_1) { + int scale = inst.src2 & 0x1F; + int rmode = inst.src2 >> 6; + + CVTSS2SD(regs_.FX(inst.dest), regs_.F(inst.src1)); + if (scale != 0) + MULSD(regs_.FX(inst.dest), M(&constants.mulTableVf2i[scale])); // rip accessible + + // On NAN, we want maxInt anyway, so let's let it be the second param. + MAXSD(regs_.FX(inst.dest), M(constants.minIntAsDouble)); // rip accessible + MINSD(regs_.FX(inst.dest), M(constants.maxIntAsDouble)); // rip accessible + + switch (rmode) { + case 0: + ROUNDNEARPD(regs_.FX(inst.dest), regs_.F(inst.dest)); + CVTPD2DQ(regs_.FX(inst.dest), regs_.F(inst.dest)); + break; + + case 1: + CVTTPD2DQ(regs_.FX(inst.dest), regs_.F(inst.dest)); + break; + + case 2: + ROUNDCEILPD(regs_.FX(inst.dest), regs_.F(inst.dest)); + CVTPD2DQ(regs_.FX(inst.dest), regs_.F(inst.dest)); + break; + + case 3: + ROUNDFLOORPD(regs_.FX(inst.dest), regs_.F(inst.dest)); + CVTPD2DQ(regs_.FX(inst.dest), regs_.F(inst.dest)); + break; + } + } else { + int scale = inst.src2 & 0x1F; + int rmode = inst.src2 >> 6; + + int setMXCSR = -1; + bool useTrunc = false; + switch (rmode) { + case 0: + // TODO: Could skip if hasSetRounding, but we don't have the flag. + setMXCSR = 0; + break; + case 1: + useTrunc = true; + break; + case 2: + setMXCSR = 2; + break; + case 3: + setMXCSR = 1; + break; + } + + // Except for truncate, we need to update MXCSR to our preferred rounding mode. + // TODO: Might be possible to cache this and update between instructions? + // Probably kinda expensive to switch each time... + if (setMXCSR != -1) { + STMXCSR(MDisp(CTXREG, mxcsrTempOffset)); + MOV(32, R(SCRATCH1), MDisp(CTXREG, mxcsrTempOffset)); + AND(32, R(SCRATCH1), Imm32(~(3 << 13))); + if (setMXCSR != 0) { + OR(32, R(SCRATCH1), Imm32(setMXCSR << 13)); + } + MOV(32, MDisp(CTXREG, tempOffset), R(SCRATCH1)); + LDMXCSR(MDisp(CTXREG, tempOffset)); + } + + CVTSS2SD(regs_.FX(inst.dest), regs_.F(inst.src1)); + if (scale != 0) + MULSD(regs_.FX(inst.dest), M(&constants.mulTableVf2i[scale])); + + // On NAN, we want maxInt anyway, so let's let it be the second param. + MAXSD(regs_.FX(inst.dest), M(constants.minIntAsDouble)); + MINSD(regs_.FX(inst.dest), M(constants.maxIntAsDouble)); + + if (useTrunc) { + CVTTSD2SI(SCRATCH1, regs_.F(inst.dest)); + } else { + CVTSD2SI(SCRATCH1, regs_.F(inst.dest)); + } + + MOVD_xmm(regs_.FX(inst.dest), R(SCRATCH1)); + + // Return MXCSR to its previous value. + if (setMXCSR != -1) { + LDMXCSR(MDisp(CTXREG, mxcsrTempOffset)); + } + } break; case IROp::FCvtScaledSW: diff --git a/Core/MIPS/x86/X64IRJit.h b/Core/MIPS/x86/X64IRJit.h index a30268f554..9a58f645f1 100644 --- a/Core/MIPS/x86/X64IRJit.h +++ b/Core/MIPS/x86/X64IRJit.h @@ -148,6 +148,9 @@ private: const void *negativeOnes; const void *qNAN; const float *mulTableVi2f; + const double *mulTableVf2i; + const double *minIntAsDouble; + const double *maxIntAsDouble; const Float4Constant *vec4InitValues; }; Constants constants; diff --git a/Core/MIPS/x86/X64IRRegCache.h b/Core/MIPS/x86/X64IRRegCache.h index d36463d876..953c92e759 100644 --- a/Core/MIPS/x86/X64IRRegCache.h +++ b/Core/MIPS/x86/X64IRRegCache.h @@ -42,6 +42,7 @@ static constexpr auto downcountOffset = offsetof(MIPSState, downcount) - 128; static constexpr auto tempOffset = offsetof(MIPSState, temp) - 128; static constexpr auto fcr31Offset = offsetof(MIPSState, fcr31) - 128; static constexpr auto pcOffset = offsetof(MIPSState, pc) - 128; +static constexpr auto mxcsrTempOffset = offsetof(MIPSState, mxcsrTemp) - 128; enum class X64Map : uint8_t { NONE = 0,