Merge pull request #834 from FioraAeterna/fixfmulrounding
JIT64: Fix fmul rounding issues
This commit is contained in:
commit
ebf1b98106
|
@ -94,6 +94,13 @@ inline double ForceDouble(double d)
|
|||
return d;
|
||||
}
|
||||
|
||||
inline double Force25Bit(double d)
|
||||
{
|
||||
u64 di = *(u64*)&d;
|
||||
di = (di & 0xFFFFFFFFF8000000ULL) + (di & 0x8000000);
|
||||
return *(double*)&di;
|
||||
}
|
||||
|
||||
// these functions allow globally modify operations behaviour
|
||||
// also, these may be used to set flags like FR, FI, OX, UX
|
||||
|
||||
|
|
|
@ -297,7 +297,8 @@ void Interpreter::fmulx(UGeckoInstruction _inst)
|
|||
}
|
||||
void Interpreter::fmulsx(UGeckoInstruction _inst)
|
||||
{
|
||||
double d_value = NI_mul(rPS0(_inst.FA), rPS0(_inst.FC));
|
||||
double c_value = Force25Bit(rPS0(_inst.FC));
|
||||
double d_value = NI_mul(rPS0(_inst.FA), c_value);
|
||||
rPS0(_inst.FD) = rPS1(_inst.FD) = ForceSingle(d_value);
|
||||
//FPSCR.FI = d_value != rPS0(_inst.FD);
|
||||
FPSCR.FI = 0;
|
||||
|
@ -320,7 +321,8 @@ void Interpreter::fmaddx(UGeckoInstruction _inst)
|
|||
|
||||
void Interpreter::fmaddsx(UGeckoInstruction _inst)
|
||||
{
|
||||
double d_value = NI_madd( rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB) );
|
||||
double c_value = Force25Bit(rPS0(_inst.FC));
|
||||
double d_value = NI_madd(rPS0(_inst.FA), c_value, rPS0(_inst.FB));
|
||||
rPS0(_inst.FD) = rPS1(_inst.FD) = ForceSingle(d_value);
|
||||
FPSCR.FI = d_value != rPS0(_inst.FD);
|
||||
FPSCR.FR = 0;
|
||||
|
|
|
@ -264,8 +264,10 @@ void Interpreter::ps_add(UGeckoInstruction _inst)
|
|||
|
||||
void Interpreter::ps_mul(UGeckoInstruction _inst)
|
||||
{
|
||||
rPS0(_inst.FD) = ForceSingle(NI_mul(rPS0(_inst.FA), rPS0(_inst.FC)));
|
||||
rPS1(_inst.FD) = ForceSingle(NI_mul(rPS1(_inst.FA), rPS1(_inst.FC)));
|
||||
double c0 = Force25Bit(rPS0(_inst.FC));
|
||||
double c1 = Force25Bit(rPS1(_inst.FC));
|
||||
rPS0(_inst.FD) = ForceSingle(NI_mul(rPS0(_inst.FA), c0));
|
||||
rPS1(_inst.FD) = ForceSingle(NI_mul(rPS1(_inst.FA), c1));
|
||||
UpdateFPRF(rPS0(_inst.FD));
|
||||
|
||||
if (_inst.Rc)
|
||||
|
@ -275,8 +277,10 @@ void Interpreter::ps_mul(UGeckoInstruction _inst)
|
|||
|
||||
void Interpreter::ps_msub(UGeckoInstruction _inst)
|
||||
{
|
||||
rPS0(_inst.FD) = ForceSingle(NI_msub(rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB)));
|
||||
rPS1(_inst.FD) = ForceSingle(NI_msub(rPS1(_inst.FA), rPS1(_inst.FC), rPS1(_inst.FB)));
|
||||
double c0 = Force25Bit(rPS0(_inst.FC));
|
||||
double c1 = Force25Bit(rPS1(_inst.FC));
|
||||
rPS0(_inst.FD) = ForceSingle(NI_msub(rPS0(_inst.FA), c0, rPS0(_inst.FB)));
|
||||
rPS1(_inst.FD) = ForceSingle(NI_msub(rPS1(_inst.FA), c1, rPS1(_inst.FB)));
|
||||
UpdateFPRF(rPS0(_inst.FD));
|
||||
|
||||
if (_inst.Rc)
|
||||
|
@ -285,8 +289,10 @@ void Interpreter::ps_msub(UGeckoInstruction _inst)
|
|||
|
||||
void Interpreter::ps_madd(UGeckoInstruction _inst)
|
||||
{
|
||||
rPS0(_inst.FD) = ForceSingle(NI_madd(rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB)));
|
||||
rPS1(_inst.FD) = ForceSingle(NI_madd(rPS1(_inst.FA), rPS1(_inst.FC), rPS1(_inst.FB)));
|
||||
double c0 = Force25Bit(rPS0(_inst.FC));
|
||||
double c1 = Force25Bit(rPS1(_inst.FC));
|
||||
rPS0(_inst.FD) = ForceSingle(NI_madd(rPS0(_inst.FA), c0, rPS0(_inst.FB)));
|
||||
rPS1(_inst.FD) = ForceSingle(NI_madd(rPS1(_inst.FA), c1, rPS1(_inst.FB)));
|
||||
UpdateFPRF(rPS0(_inst.FD));
|
||||
|
||||
if (_inst.Rc)
|
||||
|
@ -295,8 +301,10 @@ void Interpreter::ps_madd(UGeckoInstruction _inst)
|
|||
|
||||
void Interpreter::ps_nmsub(UGeckoInstruction _inst)
|
||||
{
|
||||
rPS0(_inst.FD) = ForceSingle( -NI_msub( rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB) ) );
|
||||
rPS1(_inst.FD) = ForceSingle( -NI_msub( rPS1(_inst.FA), rPS1(_inst.FC), rPS1(_inst.FB) ) );
|
||||
double c0 = Force25Bit(rPS0(_inst.FC));
|
||||
double c1 = Force25Bit(rPS1(_inst.FC));
|
||||
rPS0(_inst.FD) = ForceSingle(-NI_msub(rPS0(_inst.FA), c0, rPS0(_inst.FB)));
|
||||
rPS1(_inst.FD) = ForceSingle(-NI_msub(rPS1(_inst.FA), c1, rPS1(_inst.FB)));
|
||||
UpdateFPRF(rPS0(_inst.FD));
|
||||
|
||||
if (_inst.Rc)
|
||||
|
@ -305,8 +313,10 @@ void Interpreter::ps_nmsub(UGeckoInstruction _inst)
|
|||
|
||||
void Interpreter::ps_nmadd(UGeckoInstruction _inst)
|
||||
{
|
||||
rPS0(_inst.FD) = ForceSingle( -NI_madd( rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB) ) );
|
||||
rPS1(_inst.FD) = ForceSingle( -NI_madd( rPS1(_inst.FA), rPS1(_inst.FC), rPS1(_inst.FB) ) );
|
||||
double c0 = Force25Bit(rPS0(_inst.FC));
|
||||
double c1 = Force25Bit(rPS1(_inst.FC));
|
||||
rPS0(_inst.FD) = ForceSingle(-NI_madd(rPS0(_inst.FA), c0, rPS0(_inst.FB)));
|
||||
rPS1(_inst.FD) = ForceSingle(-NI_madd(rPS1(_inst.FA), c1, rPS1(_inst.FB)));
|
||||
UpdateFPRF(rPS0(_inst.FD));
|
||||
|
||||
if (_inst.Rc)
|
||||
|
@ -339,8 +349,9 @@ void Interpreter::ps_sum1(UGeckoInstruction _inst)
|
|||
|
||||
void Interpreter::ps_muls0(UGeckoInstruction _inst)
|
||||
{
|
||||
double p0 = ForceSingle(NI_mul(rPS0(_inst.FA), rPS0(_inst.FC)));
|
||||
double p1 = ForceSingle(NI_mul(rPS1(_inst.FA), rPS0(_inst.FC)));
|
||||
double c0 = Force25Bit(rPS1(_inst.FC));
|
||||
double p0 = ForceSingle(NI_mul(rPS0(_inst.FA), c0));
|
||||
double p1 = ForceSingle(NI_mul(rPS1(_inst.FA), c0));
|
||||
rPS0(_inst.FD) = p0;
|
||||
rPS1(_inst.FD) = p1;
|
||||
UpdateFPRF(rPS0(_inst.FD));
|
||||
|
@ -351,8 +362,9 @@ void Interpreter::ps_muls0(UGeckoInstruction _inst)
|
|||
|
||||
void Interpreter::ps_muls1(UGeckoInstruction _inst)
|
||||
{
|
||||
double p0 = ForceSingle(NI_mul(rPS0(_inst.FA), rPS1(_inst.FC)));
|
||||
double p1 = ForceSingle(NI_mul(rPS1(_inst.FA), rPS1(_inst.FC)));
|
||||
double c1 = Force25Bit(rPS1(_inst.FC));
|
||||
double p0 = ForceSingle(NI_mul(rPS0(_inst.FA), c1));
|
||||
double p1 = ForceSingle(NI_mul(rPS1(_inst.FA), c1));
|
||||
rPS0(_inst.FD) = p0;
|
||||
rPS1(_inst.FD) = p1;
|
||||
UpdateFPRF(rPS0(_inst.FD));
|
||||
|
@ -363,8 +375,10 @@ void Interpreter::ps_muls1(UGeckoInstruction _inst)
|
|||
|
||||
void Interpreter::ps_madds0(UGeckoInstruction _inst)
|
||||
{
|
||||
double p0 = ForceSingle( NI_madd( rPS0(_inst.FA), rPS0(_inst.FC), rPS0(_inst.FB)) );
|
||||
double p1 = ForceSingle( NI_madd( rPS1(_inst.FA), rPS0(_inst.FC), rPS1(_inst.FB)) );
|
||||
double c0 = Force25Bit(rPS0(_inst.FC));
|
||||
double c1 = Force25Bit(rPS1(_inst.FC));
|
||||
double p0 = ForceSingle(NI_madd(rPS0(_inst.FA), c0, rPS0(_inst.FB)));
|
||||
double p1 = ForceSingle(NI_madd(rPS1(_inst.FA), c1, rPS1(_inst.FB)));
|
||||
rPS0(_inst.FD) = p0;
|
||||
rPS1(_inst.FD) = p1;
|
||||
UpdateFPRF(rPS0(_inst.FD));
|
||||
|
@ -375,8 +389,10 @@ void Interpreter::ps_madds0(UGeckoInstruction _inst)
|
|||
|
||||
void Interpreter::ps_madds1(UGeckoInstruction _inst)
|
||||
{
|
||||
double p0 = ForceSingle( NI_madd( rPS0(_inst.FA), rPS1(_inst.FC), rPS0(_inst.FB)) );
|
||||
double p1 = ForceSingle( NI_madd( rPS1(_inst.FA), rPS1(_inst.FC), rPS1(_inst.FB)) );
|
||||
double c0 = Force25Bit(rPS0(_inst.FC));
|
||||
double c1 = Force25Bit(rPS1(_inst.FC));
|
||||
double p0 = ForceSingle(NI_madd(rPS0(_inst.FA), c0, rPS0(_inst.FB)));
|
||||
double p1 = ForceSingle(NI_madd(rPS1(_inst.FA), c1, rPS1(_inst.FB)));
|
||||
rPS0(_inst.FD) = p0;
|
||||
rPS1(_inst.FD) = p1;
|
||||
UpdateFPRF(rPS0(_inst.FD));
|
||||
|
|
|
@ -117,10 +117,10 @@ public:
|
|||
// is set or not.
|
||||
Gen::FixupBranch JumpIfCRFieldBit(int field, int bit, bool jump_if_set = true);
|
||||
|
||||
void tri_op(int d, int a, int b, bool reversible, void (Gen::XEmitter::*op)(Gen::X64Reg, Gen::OpArg));
|
||||
void tri_op(int d, int a, int b, bool reversible, void (Gen::XEmitter::*op)(Gen::X64Reg, Gen::OpArg), bool roundRHS = false);
|
||||
typedef u32 (*Operation)(u32 a, u32 b);
|
||||
void regimmop(int d, int a, bool binary, u32 value, Operation doop, void (Gen::XEmitter::*op)(int, const Gen::OpArg&, const Gen::OpArg&), bool Rc = false, bool carry = false);
|
||||
void fp_tri_op(int d, int a, int b, bool reversible, bool single, void (Gen::XEmitter::*op)(Gen::X64Reg, Gen::OpArg));
|
||||
void fp_tri_op(int d, int a, int b, bool reversible, bool single, void (Gen::XEmitter::*op)(Gen::X64Reg, Gen::OpArg), bool roundRHS = false);
|
||||
|
||||
// OPCODES
|
||||
void unknown_instruction(UGeckoInstruction _inst);
|
||||
|
|
|
@ -14,10 +14,28 @@ static const u64 GC_ALIGNED16(psSignBits2[2]) = {0x8000000000000000ULL, 0x800000
|
|||
static const u64 GC_ALIGNED16(psAbsMask2[2]) = {0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL};
|
||||
static const double GC_ALIGNED16(half_qnan_and_s32_max[2]) = {0x7FFFFFFF, -0x80000};
|
||||
|
||||
void Jit64::fp_tri_op(int d, int a, int b, bool reversible, bool single, void (XEmitter::*op)(Gen::X64Reg, Gen::OpArg))
|
||||
void Jit64::fp_tri_op(int d, int a, int b, bool reversible, bool single, void (XEmitter::*op)(Gen::X64Reg, Gen::OpArg), bool roundRHS)
|
||||
{
|
||||
fpr.Lock(d, a, b);
|
||||
if (d == a)
|
||||
if (roundRHS)
|
||||
{
|
||||
if (d == a)
|
||||
{
|
||||
fpr.BindToRegister(d, true);
|
||||
MOVSD(XMM0, fpr.R(b));
|
||||
Force25BitPrecision(XMM0, XMM1);
|
||||
(this->*op)(fpr.RX(d), R(XMM0));
|
||||
}
|
||||
else
|
||||
{
|
||||
fpr.BindToRegister(d, d == b);
|
||||
if (d != b)
|
||||
MOVSD(fpr.RX(d), fpr.R(b));
|
||||
Force25BitPrecision(fpr.RX(d), XMM0);
|
||||
(this->*op)(fpr.RX(d), fpr.R(a));
|
||||
}
|
||||
}
|
||||
else if (d == a)
|
||||
{
|
||||
fpr.BindToRegister(d, true);
|
||||
if (!single)
|
||||
|
@ -88,7 +106,7 @@ void Jit64::fp_arith(UGeckoInstruction inst)
|
|||
case 18: fp_tri_op(inst.FD, inst.FA, inst.FB, false, single, &XEmitter::DIVSD); break; //div
|
||||
case 20: fp_tri_op(inst.FD, inst.FA, inst.FB, false, single, &XEmitter::SUBSD); break; //sub
|
||||
case 21: fp_tri_op(inst.FD, inst.FA, inst.FB, true, single, &XEmitter::ADDSD); break; //add
|
||||
case 25: fp_tri_op(inst.FD, inst.FA, inst.FC, true, single, &XEmitter::MULSD); break; //mul
|
||||
case 25: fp_tri_op(inst.FD, inst.FA, inst.FC, true, single, &XEmitter::MULSD, single); break; //mul
|
||||
default:
|
||||
_assert_msg_(DYNA_REC, 0, "fp_arith WTF!!!");
|
||||
}
|
||||
|
@ -111,24 +129,25 @@ void Jit64::fmaddXX(UGeckoInstruction inst)
|
|||
int d = inst.FD;
|
||||
|
||||
fpr.Lock(a, b, c, d);
|
||||
MOVSD(XMM0, fpr.R(a));
|
||||
MOVSD(XMM0, fpr.R(c));
|
||||
Force25BitPrecision(XMM0, XMM1);
|
||||
switch (inst.SUBOP5)
|
||||
{
|
||||
case 28: //msub
|
||||
MULSD(XMM0, fpr.R(c));
|
||||
MULSD(XMM0, fpr.R(a));
|
||||
SUBSD(XMM0, fpr.R(b));
|
||||
break;
|
||||
case 29: //madd
|
||||
MULSD(XMM0, fpr.R(c));
|
||||
MULSD(XMM0, fpr.R(a));
|
||||
ADDSD(XMM0, fpr.R(b));
|
||||
break;
|
||||
case 30: //nmsub
|
||||
MULSD(XMM0, fpr.R(c));
|
||||
MULSD(XMM0, fpr.R(a));
|
||||
SUBSD(XMM0, fpr.R(b));
|
||||
PXOR(XMM0, M((void*)&psSignBits2));
|
||||
break;
|
||||
case 31: //nmadd
|
||||
MULSD(XMM0, fpr.R(c));
|
||||
MULSD(XMM0, fpr.R(a));
|
||||
ADDSD(XMM0, fpr.R(b));
|
||||
PXOR(XMM0, M((void*)&psSignBits2));
|
||||
break;
|
||||
|
|
|
@ -113,11 +113,29 @@ add a,b,a
|
|||
*/
|
||||
|
||||
//There's still a little bit more optimization that can be squeezed out of this
|
||||
void Jit64::tri_op(int d, int a, int b, bool reversible, void (XEmitter::*op)(X64Reg, OpArg))
|
||||
void Jit64::tri_op(int d, int a, int b, bool reversible, void (XEmitter::*op)(X64Reg, OpArg), bool roundRHS)
|
||||
{
|
||||
fpr.Lock(d, a, b);
|
||||
|
||||
if (d == a)
|
||||
if (roundRHS)
|
||||
{
|
||||
if (d == a)
|
||||
{
|
||||
fpr.BindToRegister(d, true);
|
||||
MOVAPD(XMM0, fpr.R(b));
|
||||
Force25BitPrecision(XMM0, XMM1);
|
||||
(this->*op)(fpr.RX(d), R(XMM0));
|
||||
}
|
||||
else
|
||||
{
|
||||
fpr.BindToRegister(d, d == b);
|
||||
if (d != b)
|
||||
MOVAPD(fpr.RX(d), fpr.R(b));
|
||||
Force25BitPrecision(fpr.RX(d), XMM0);
|
||||
(this->*op)(fpr.RX(d), fpr.R(a));
|
||||
}
|
||||
}
|
||||
else if (d == a)
|
||||
{
|
||||
fpr.BindToRegister(d, true);
|
||||
(this->*op)(fpr.RX(d), fpr.R(b));
|
||||
|
@ -166,7 +184,7 @@ void Jit64::ps_arith(UGeckoInstruction inst)
|
|||
tri_op(inst.FD, inst.FA, inst.FB, true, &XEmitter::ADDPD);
|
||||
break;
|
||||
case 25: // mul
|
||||
tri_op(inst.FD, inst.FA, inst.FC, true, &XEmitter::MULPD);
|
||||
tri_op(inst.FD, inst.FA, inst.FC, true, &XEmitter::MULPD, true);
|
||||
break;
|
||||
default:
|
||||
_assert_msg_(DYNA_REC, 0, "ps_arith WTF!!!");
|
||||
|
@ -230,15 +248,17 @@ void Jit64::ps_muls(UGeckoInstruction inst)
|
|||
case 12:
|
||||
// Single multiply scalar high
|
||||
// TODO - faster version for when regs are different
|
||||
MOVAPD(XMM0, fpr.R(a));
|
||||
MOVDDUP(XMM1, fpr.R(c));
|
||||
Force25BitPrecision(XMM1, XMM0);
|
||||
MOVAPD(XMM0, fpr.R(a));
|
||||
MULPD(XMM0, R(XMM1));
|
||||
MOVAPD(fpr.R(d), XMM0);
|
||||
break;
|
||||
case 13:
|
||||
// TODO - faster version for when regs are different
|
||||
MOVAPD(XMM0, fpr.R(a));
|
||||
MOVAPD(XMM1, fpr.R(c));
|
||||
Force25BitPrecision(XMM1, XMM0);
|
||||
MOVAPD(XMM0, fpr.R(a));
|
||||
SHUFPD(XMM1, R(XMM1), 3); // copy higher to lower
|
||||
MULPD(XMM0, R(XMM1));
|
||||
MOVAPD(fpr.R(d), XMM0);
|
||||
|
@ -300,35 +320,46 @@ void Jit64::ps_maddXX(UGeckoInstruction inst)
|
|||
int d = inst.FD;
|
||||
fpr.Lock(a,b,c,d);
|
||||
|
||||
MOVAPD(XMM0, fpr.R(a));
|
||||
switch (inst.SUBOP5)
|
||||
{
|
||||
case 14: //madds0
|
||||
MOVDDUP(XMM1, fpr.R(c));
|
||||
Force25BitPrecision(XMM1, XMM0);
|
||||
MOVAPD(XMM0, fpr.R(a));
|
||||
MULPD(XMM0, R(XMM1));
|
||||
ADDPD(XMM0, fpr.R(b));
|
||||
break;
|
||||
case 15: //madds1
|
||||
MOVAPD(XMM1, fpr.R(c));
|
||||
SHUFPD(XMM1, R(XMM1), 3); // copy higher to lower
|
||||
Force25BitPrecision(XMM1, XMM0);
|
||||
MOVAPD(XMM0, fpr.R(a));
|
||||
MULPD(XMM0, R(XMM1));
|
||||
ADDPD(XMM0, fpr.R(b));
|
||||
break;
|
||||
case 28: //msub
|
||||
MULPD(XMM0, fpr.R(c));
|
||||
MOVAPD(XMM0, fpr.R(c));
|
||||
Force25BitPrecision(XMM0, XMM1);
|
||||
MULPD(XMM0, fpr.R(a));
|
||||
SUBPD(XMM0, fpr.R(b));
|
||||
break;
|
||||
case 29: //madd
|
||||
MULPD(XMM0, fpr.R(c));
|
||||
MOVAPD(XMM0, fpr.R(c));
|
||||
Force25BitPrecision(XMM0, XMM1);
|
||||
MULPD(XMM0, fpr.R(a));
|
||||
ADDPD(XMM0, fpr.R(b));
|
||||
break;
|
||||
case 30: //nmsub
|
||||
MULPD(XMM0, fpr.R(c));
|
||||
MOVAPD(XMM0, fpr.R(c));
|
||||
Force25BitPrecision(XMM0, XMM1);
|
||||
MULPD(XMM0, fpr.R(a));
|
||||
SUBPD(XMM0, fpr.R(b));
|
||||
PXOR(XMM0, M((void*)&psSignBits));
|
||||
break;
|
||||
case 31: //nmadd
|
||||
MULPD(XMM0, fpr.R(c));
|
||||
MOVAPD(XMM0, fpr.R(c));
|
||||
Force25BitPrecision(XMM0, XMM1);
|
||||
MULPD(XMM0, fpr.R(a));
|
||||
ADDPD(XMM0, fpr.R(b));
|
||||
PXOR(XMM0, M((void*)&psSignBits));
|
||||
break;
|
||||
|
|
|
@ -519,6 +519,25 @@ void EmuCodeBlock::ForceSinglePrecisionP(X64Reg xmm)
|
|||
}
|
||||
}
|
||||
|
||||
static const u64 GC_ALIGNED16(psMantissaTruncate[2]) = {0xFFFFFFFFF8000000ULL, 0xFFFFFFFFF8000000ULL};
|
||||
static const u64 GC_ALIGNED16(psRoundBit[2]) = {0x8000000, 0x8000000};
|
||||
|
||||
// Emulate the odd truncation/rounding that the PowerPC does on the RHS operand before
|
||||
// a single precision multiply. To be precise, it drops the low 28 bits of the mantissa,
|
||||
// rounding to nearest as it does.
|
||||
// It needs a temp, so let the caller pass that in.
|
||||
void EmuCodeBlock::Force25BitPrecision(X64Reg xmm, X64Reg tmp)
|
||||
{
|
||||
if (jit->jo.accurateSinglePrecision)
|
||||
{
|
||||
// mantissa = (mantissa & ~0xFFFFFFF) + ((mantissa & (1ULL << 27)) << 1);
|
||||
MOVAPD(tmp, R(xmm));
|
||||
PAND(xmm, M((void*)&psMantissaTruncate));
|
||||
PAND(tmp, M((void*)&psRoundBit));
|
||||
PADDQ(xmm, R(tmp));
|
||||
}
|
||||
}
|
||||
|
||||
static u32 GC_ALIGNED16(temp32);
|
||||
static u64 GC_ALIGNED16(temp64);
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ public:
|
|||
|
||||
void ForceSinglePrecisionS(Gen::X64Reg xmm);
|
||||
void ForceSinglePrecisionP(Gen::X64Reg xmm);
|
||||
void Force25BitPrecision(Gen::X64Reg xmm, Gen::X64Reg tmp);
|
||||
|
||||
// EAX might get trashed
|
||||
void ConvertSingleToDouble(Gen::X64Reg dst, Gen::X64Reg src, bool src_is_gpr = false);
|
||||
|
|
Loading…
Reference in New Issue