Fix FXTRACT for 0.0 and -0.0

Fixes fxtract by returning the correct values for 0.0 and -0.0. We moved the split of fxtract into _sig and _exp, to the opcode dispatcher, to ease some comparisons.

Also removed the IR node F80XTRACTStack which is not needed anymore.
This commit is contained in:
Paulo Matos 2024-09-30 16:09:48 +02:00
parent 49087007be
commit 10ec6b63b6
7 changed files with 58 additions and 46 deletions

View File

@ -233,6 +233,10 @@ struct FEX_PACKED X80SoftFloat {
return Result;
#else
// Zero is a special case, the significand for +/- 0 is +/- zero.
if (lhs.Exponent == 0x0 && lhs.Significand == 0x0) {
return lhs;
}
X80SoftFloat Tmp = lhs;
Tmp.Exponent = 0x3FFF;
Tmp.Sign = lhs.Sign;
@ -256,6 +260,12 @@ struct FEX_PACKED X80SoftFloat {
return Result;
#else
// Zero is a special case, the exponent is always -inf
if (lhs.Exponent == 0x0 && lhs.Significand == 0x0) {
X80SoftFloat Result(1, 0x7FFFUL, 0x8000'0000'0000'0000UL);
return Result;
}
int32_t TrueExp = lhs.Exponent - ExponentBias;
return i32_to_extF80(TrueExp);
#endif

View File

@ -5535,7 +5535,7 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPD(0xD9, 0xF1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87FYL2X, false>},
{OPD(0xD9, 0xF2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80PTANSTACK, true>},
{OPD(0xD9, 0xF3), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80ATANSTACK, false>},
{OPD(0xD9, 0xF4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80XTRACTSTACK, false>},
{OPD(0xD9, 0xF4), 1, &OpDispatchBuilder::X87FXTRACTF64},
{OPD(0xD9, 0xF5), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80FPREM1STACK, true>},
{OPD(0xD9, 0xF6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87ModifySTP, false>},
{OPD(0xD9, 0xF7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87ModifySTP, true>},
@ -5765,7 +5765,7 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
{OPD(0xD9, 0xF1), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87FYL2X, false>},
{OPD(0xD9, 0xF2), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80PTANSTACK, true>},
{OPD(0xD9, 0xF3), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80ATANSTACK, false>},
{OPD(0xD9, 0xF4), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80XTRACTSTACK, false>},
{OPD(0xD9, 0xF4), 1, &OpDispatchBuilder::X87FXTRACT},
{OPD(0xD9, 0xF5), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87OpHelper, OP_F80FPREM1STACK, true>},
{OPD(0xD9, 0xF6), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87ModifySTP, false>},
{OPD(0xD9, 0xF7), 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::X87ModifySTP, true>},

View File

@ -735,6 +735,7 @@ public:
void X87FNSAVE(OpcodeArgs);
void X87FRSTOR(OpcodeArgs);
void X87FXAM(OpcodeArgs);
void X87FXTRACT(OpcodeArgs);
void X87FCMOV(OpcodeArgs);
void X87EMMS(OpcodeArgs);
void X87FFREE(OpcodeArgs);
@ -768,7 +769,6 @@ public:
void FABSF64(OpcodeArgs);
void FTSTF64(OpcodeArgs);
void FRNDINTF64(OpcodeArgs);
void FXTRACTF64(OpcodeArgs);
void FNINITF64(OpcodeArgs);
void FSQRTF64(OpcodeArgs);
void X87UnaryOpF64(OpcodeArgs, FEXCore::IR::IROps IROp);
@ -780,6 +780,7 @@ public:
void X87FNSAVEF64(OpcodeArgs);
void X87FRSTORF64(OpcodeArgs);
void X87FXAMF64(OpcodeArgs);
void X87FXTRACTF64(OpcodeArgs);
void X87LDENVF64(OpcodeArgs);
void FCOMIF64(OpcodeArgs, size_t width, bool Integer, FCOMIFlags whichflags, bool poptwice);

View File

@ -804,4 +804,14 @@ void OpDispatchBuilder::X87FXAM(OpcodeArgs) {
SetRFLAG<FEXCore::X86State::X87FLAG_C3_LOC>(C3);
}
void OpDispatchBuilder::X87FXTRACT(OpcodeArgs) {
auto Top = _ReadStackValue(0);
_PopStackDestroy();
auto Exp = _F80XTRACT_EXP(Top);
auto Sig = _F80XTRACT_SIG(Top);
_PushStack(Exp, Exp, 80, true);
_PushStack(Sig, Sig, 80, true);
}
} // namespace FEXCore::IR

View File

@ -8,6 +8,7 @@ $end_info$
#include "Interface/Core/OpcodeDispatcher.h"
#include "Interface/Core/X86Tables/X86Tables.h"
#include "Interface/IR/IR.h"
#include <FEXCore/Core/CoreState.h>
#include <FEXCore/Core/X86Enums.h>
@ -519,4 +520,37 @@ void OpDispatchBuilder::X87FRSTORF64(OpcodeArgs) {
_StoreContextIndexed(Reg, Top, 8, MMBaseOffset(), 16, FPRClass);
}
void OpDispatchBuilder::X87FXTRACTF64(OpcodeArgs) {
// Split node into SIG and EXP while handling the special zero case.
// i.e. if val == 0.0, then sig = 0.0, exp = -inf
// if val == -0.0, then sig = -0.0, exp = -inf
// otherwise we just extract the 64-bit sig and exp as normal.
Ref Node = _ReadStackValue(0);
Ref Gpr = _VExtractToGPR(8, 8, Node, 0);
// zero case
Ref ExpZV = _VCastFromGPR(8, 8, _Constant(0xfff0'0000'0000'0000UL));
Ref SigZV = Node;
// non zero case
Ref ExpNZ = _Bfe(OpSize::i64Bit, 11, 52, Gpr);
ExpNZ = _Sub(OpSize::i64Bit, ExpNZ, _Constant(1023));
Ref ExpNZV = _Float_FromGPR_S(8, 8, ExpNZ);
Ref SigNZ = _And(OpSize::i64Bit, Gpr, _Constant(0x800f'ffff'ffff'ffffLL));
SigNZ = _Or(OpSize::i64Bit, SigNZ, _Constant(0x3ff0'0000'0000'0000LL));
Ref SigNZV = _VCastFromGPR(8, 8, SigNZ);
// Comparison and select to push onto stack
SaveNZCV();
_TestNZ(OpSize::i64Bit, Gpr, _Constant(0x7fff'ffff'ffff'ffffUL));
Ref Sig = _NZCVSelectV(8, {COND_EQ}, SigZV, SigNZV);
Ref Exp = _NZCVSelectV(8, {COND_EQ}, ExpZV, ExpNZV);
_PopStackDestroy();
_PushStack(Exp, Exp, 64, true);
_PushStack(Sig, Sig, 64, true);
}
} // namespace FEXCore::IR

View File

@ -3051,10 +3051,6 @@
"DestSize": "16",
"JITDispatch": false
},
"F80XTRACTStack": {
"X87": true,
"HasSideEffects": true
},
"FPR = F80XTRACT_EXP FPR:$X80Src": {
"DestSize": "16",
"JITDispatch": false

View File

@ -156,7 +156,6 @@ private:
bool ReducedPrecisionMode;
// Helpers
std::tuple<Ref, Ref> SplitF64SigExp(Ref Node);
Ref RotateRight8(uint32_t V, Ref Amount);
// Handles a Unary operation.
@ -518,20 +517,6 @@ Ref X87StackOptimization::SynchronizeStackValues() {
return TopValue;
}
std::tuple<Ref, Ref> X87StackOptimization::SplitF64SigExp(Ref Node) {
Ref Gpr = IREmit->_VExtractToGPR(8, 8, Node, 0);
Ref Exp = IREmit->_And(OpSize::i64Bit, Gpr, GetConstant(0x7ff0000000000000LL));
Exp = IREmit->_Lshr(OpSize::i64Bit, Exp, GetConstant(52));
Exp = IREmit->_Sub(OpSize::i64Bit, Exp, GetConstant(1023));
Exp = IREmit->_Float_FromGPR_S(8, 8, Exp);
Ref Sig = IREmit->_And(OpSize::i64Bit, Gpr, GetConstant(0x800fffffffffffffLL));
Sig = IREmit->_Or(OpSize::i64Bit, Sig, GetConstant(0x3ff0000000000000LL));
Sig = IREmit->_VCastFromGPR(8, 8, Sig);
return std::tuple {Exp, Sig};
}
void X87StackOptimization::Run(IREmitter* Emit) {
FEXCORE_PROFILE_SCOPED("PassManager::x87StackOpt");
@ -964,30 +949,6 @@ void X87StackOptimization::Run(IREmitter* Emit) {
break;
}
case OP_F80XTRACTSTACK: {
Ref St0 = LoadStackValue();
Ref Exp {};
Ref Sig {};
if (ReducedPrecisionMode) {
std::tie(Exp, Sig) = SplitF64SigExp(St0);
} else {
Exp = IREmit->_F80XTRACT_EXP(St0);
Sig = IREmit->_F80XTRACT_SIG(St0);
}
if (SlowPath) {
// Write exp to top, update top for a push and set sig at new top.
StoreStackValueAtOffset_Slow(Exp, 0, false);
UpdateTopForPush_Slow();
StoreStackValueAtOffset_Slow(Sig);
} else {
StackData.setTop(StackMemberInfo {Exp});
StackData.push(StackMemberInfo {Sig});
}
break;
}
case OP_SYNCSTACKTOSLOW: {
// This synchronizes stack values but doesn't necessarily moves us off the FastPath!
Ref NewTop = SynchronizeStackValues();