Merge pull request #408 from Sonicadvance1/rounding_mode_host

Adds new IR ops for supporting host rounding mode conversion
This commit is contained in:
Stefanos Kornilios Mitsis Poiitidis authored and GitHub committed 2020-09-23 09:50:37 +03:00
commit 91bea781f4
7 files changed
+405 -1

No files matched your search

@@ -19,6 +19,9 @@
#include <cmath>
#include <limits>
#include <vector>
#ifdef _M_X86_64
#include <xmmintrin.h>
#endif
namespace FEXCore::CPU {
@@ -2893,6 +2896,38 @@ void InterpreterCore::ExecuteCode(FEXCore::Core::InternalThreadState *Thread) {
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VECTOR_FTOU: {
auto Op = IROp->C<IR::IROp_Vector_FToU>();
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
uint8_t Tmp[16];
uint8_t Elements = OpSize / Op->Header.ElementSize;
auto Func = [](auto a, auto min, auto max) { return a; };
switch (Op->Header.ElementSize) {
DO_VECTOR_1SRC_2TYPE_OP(4, uint32_t, float, Func, 0, 0)
DO_VECTOR_1SRC_2TYPE_OP(8, uint64_t, double, Func, 0, 0)
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VECTOR_FTOS: {
auto Op = IROp->C<IR::IROp_Vector_FToS>();
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
uint8_t Tmp[16];
uint8_t Elements = OpSize / Op->Header.ElementSize;
auto Func = [](auto a, auto min, auto max) { return a; };
switch (Op->Header.ElementSize) {
DO_VECTOR_1SRC_2TYPE_OP(4, int32_t, float, Func, 0, 0)
DO_VECTOR_1SRC_2TYPE_OP(8, int64_t, double, Func, 0, 0)
default: LogMan::Msg::A("Unknown Element Size: %d", Op->Header.ElementSize); break;
}
memcpy(GDP, Tmp, OpSize);
break;
}
case IR::OP_VUMUL: {
auto Op = IROp->C<IR::IROp_VUMul>();
void *Src1 = GetSrc<void*>(SSAData, Op->Header.Args[0]);
@@ -3698,6 +3733,30 @@ void InterpreterCore::ExecuteCode(FEXCore::Core::InternalThreadState *Thread) {
}
break;
}
case IR::OP_FLOAT_TOGPR_S: {
auto Op = IROp->C<IR::IROp_Float_ToGPR_S>();
if (Op->Header.ElementSize == 8) {
int64_t Dst = (int64_t)*GetSrc<double*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
}
else {
int32_t Dst = (int32_t)*GetSrc<float*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
}
break;
}
case IR::OP_FLOAT_TOGPR_U: {
auto Op = IROp->C<IR::IROp_Float_ToGPR_U>();
if (Op->Header.ElementSize == 8) {
uint64_t Dst = (uint64_t)*GetSrc<double*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
}
else {
uint32_t Dst = (uint32_t)*GetSrc<float*>(SSAData, Op->Header.Args[0]);
memcpy(GDP, &Dst, Op->Header.ElementSize);
}
break;
}
case IR::OP_FLOAT_FTOF: {
auto Op = IROp->C<IR::IROp_Float_FToF>();
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
@@ -4248,6 +4307,77 @@ void InterpreterCore::ExecuteCode(FEXCore::Core::InternalThreadState *Thread) {
GD = ResultFlags;
break;
}
case IR::OP_GETROUNDINGMODE: {
uint32_t GuestRounding{};
#ifdef _M_ARM_64
uint64_t Tmp{};
__asm(R"(
mrs %[Tmp], FPCR;
)"
: [Tmp] "=r" (Tmp));
// Extract the rounding
// On ARM the ordering is different than on x86
GuestRounding |= ((Tmp >> 24) & 1) ? ROUND_MODE_FLUSH_TO_ZERO : 0;
uint8_t RoundingMode = (Tmp >> 22) & 0b11;
if (RoundingMode == 0)
GuestRounding |= ROUND_MODE_NEAREST;
else if (RoundingMode == 1)
GuestRounding |= ROUND_MODE_POSITIVE_INFINITY;
else if (RoundingMode == 2)
GuestRounding |= ROUND_MODE_NEGATIVE_INFINITY;
else if (RoundingMode == 3)
GuestRounding |= ROUND_MODE_TOWARDS_ZERO;
#else
GuestRounding = _mm_getcsr();
// Extract the rounding
GuestRounding = (GuestRounding >> 13) & 0b111;
#endif
memcpy(GDP, &GuestRounding, sizeof(GuestRounding));
break;
}
case IR::OP_SETROUNDINGMODE: {
auto Op = IROp->C<IR::IROp_SetRoundingMode>();
uint8_t GuestRounding = *GetSrc<uint8_t*>(SSAData, Op->Header.Args[0]);
#ifdef _M_ARM_64
uint64_t HostRounding{};
__asm volatile(R"(
mrs %[Tmp], FPCR;
)"
: [Tmp] "=r" (HostRounding));
// Mask out the rounding
HostRounding &= ~(0b111 << 22);
HostRounding |= (GuestRounding & ROUND_MODE_FLUSH_TO_ZERO) ? (1U << 24) : 0;
uint8_t RoundingMode = GuestRounding & 0b11;
if (RoundingMode == ROUND_MODE_NEAREST)
HostRounding |= (0b00U << 22);
else if (RoundingMode == ROUND_MODE_POSITIVE_INFINITY)
HostRounding |= (0b01U << 22);
else if (RoundingMode == ROUND_MODE_NEGATIVE_INFINITY)
HostRounding |= (0b10U << 22);
else if (RoundingMode == ROUND_MODE_TOWARDS_ZERO)
HostRounding |= (0b11U << 22);
__asm volatile(R"(
msr FPCR, %[Tmp];
)"
:: [Tmp] "r" (HostRounding));
#else
uint32_t HostRounding = _mm_getcsr();
// Cut out the host rounding mode
HostRounding &= ~(0b111 << 13);
// Insert our new rounding mode
HostRounding |= GuestRounding << 13;
_mm_setcsr(HostRounding);
#endif
break;
}
default:
LogMan::Msg::A("Unknown IR Op: %d(%s)", IROp->Op, FEXCore::IR::GetName(IROp->Op).data());
break;
@@ -1032,6 +1032,22 @@ DEF_OP(Float_ToGPR_ZS) {
}
}
DEF_OP(Float_ToGPR_U) {
LogMan::Msg::D("Unimplemented");
}
DEF_OP(Float_ToGPR_S) {
auto Op = IROp->C<IR::IROp_Float_ToGPR_S>();
if (Op->Header.ElementSize == 8) {
frinti(VTMP1.D(), GetSrc(Op->Header.Args[0].ID()).D());
fcvtzs(GetReg<RA_64>(Node), VTMP1.D());
}
else {
frinti(VTMP1.S(), GetSrc(Op->Header.Args[0].ID()).S());
fcvtzs(GetReg<RA_32>(Node), VTMP1.S());
}
}
DEF_OP(FCmp) {
auto Op = IROp->C<IR::IROp_FCmp>();
@@ -1110,6 +1126,8 @@ void JITCore::RegisterALUHandlers() {
REGISTER_OP(VEXTRACTTOGPR, VExtractToGPR);
REGISTER_OP(FLOAT_TOGPR_ZU, Float_ToGPR_ZU);
REGISTER_OP(FLOAT_TOGPR_ZS, Float_ToGPR_ZS);
REGISTER_OP(FLOAT_TOGPR_U, Float_ToGPR_U);
REGISTER_OP(FLOAT_TOGPR_S, Float_ToGPR_S);
REGISTER_OP(FCMP, FCmp);
REGISTER_OP(F80CMP, F80Cmp);
@@ -132,6 +132,36 @@ DEF_OP(Vector_FToZS) {
}
}
DEF_OP(Vector_FToU) {
auto Op = IROp->C<IR::IROp_Vector_FToU>();
switch (Op->Header.ElementSize) {
case 4:
frinti(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
fcvtzu(GetDst(Node).V4S(), GetDst(Node).V4S());
break;
case 8:
frinti(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
fcvtzu(GetDst(Node).V2D(), GetDst(Node).V2D());
break;
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
}
}
DEF_OP(Vector_FToS) {
auto Op = IROp->C<IR::IROp_Vector_FToS>();
switch (Op->Header.ElementSize) {
case 4:
frinti(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
fcvtzs(GetDst(Node).V4S(), GetDst(Node).V4S());
break;
case 8:
frinti(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
fcvtzs(GetDst(Node).V2D(), GetDst(Node).V2D());
break;
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
}
}
DEF_OP(Vector_FToF) {
auto Op = IROp->C<IR::IROp_Vector_FToF>();
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
@@ -161,6 +191,8 @@ void JITCore::RegisterConversionHandlers() {
REGISTER_OP(VECTOR_STOF, Vector_SToF);
REGISTER_OP(VECTOR_FTOZU, Vector_FToZU);
REGISTER_OP(VECTOR_FTOZS, Vector_FToZS);
REGISTER_OP(VECTOR_FTOU, Vector_FToU);
REGISTER_OP(VECTOR_FTOS, Vector_FToS);
REGISTER_OP(VECTOR_FTOF, Vector_FToF);
#undef REGISTER_OP
}
@@ -280,6 +280,8 @@ private:
DEF_OP(VExtractToGPR);
DEF_OP(Float_ToGPR_ZU);
DEF_OP(Float_ToGPR_ZS);
DEF_OP(Float_ToGPR_U);
DEF_OP(Float_ToGPR_S);
DEF_OP(FCmp);
DEF_OP(F80Cmp);
@@ -323,6 +325,8 @@ private:
DEF_OP(Vector_SToF);
DEF_OP(Vector_FToZU);
DEF_OP(Vector_FToZS);
DEF_OP(Vector_FToU);
DEF_OP(Vector_FToS);
DEF_OP(Vector_FToF);
///< Flag ops
@@ -353,6 +357,8 @@ private:
DEF_OP(Phi);
DEF_OP(PhiValue);
DEF_OP(Print);
DEF_OP(GetRoundingMode);
DEF_OP(SetRoundingMode);
///< Move ops
DEF_OP(ExtractElementPair);
@@ -52,6 +52,65 @@ DEF_OP(Break) {
}
}
DEF_OP(GetRoundingMode) {
auto Op = IROp->C<IR::IROp_GetRoundingMode>();
auto Dst = GetReg<RA_64>(Node);
mrs(Dst, FPCR);
lsr(Dst, Dst, 22);
// FTZ is already in the correct location
// Rounding mode is different
and_(TMP1, Dst, 0b11);
cmp(TMP1, 1);
LoadConstant(TMP3, IR::ROUND_MODE_POSITIVE_INFINITY);
csel(TMP2, TMP3, xzr, vixl::aarch64::Condition::eq);
cmp(TMP1, 2);
LoadConstant(TMP3, IR::ROUND_MODE_NEGATIVE_INFINITY);
csel(TMP2, TMP3, TMP2, vixl::aarch64::Condition::eq);
cmp(TMP1, 3);
LoadConstant(TMP3, IR::ROUND_MODE_TOWARDS_ZERO);
csel(TMP2, TMP3, TMP2, vixl::aarch64::Condition::eq);
orr(Dst, Dst, TMP2);
bfi(Dst, TMP2, 0, 2);
}
DEF_OP(SetRoundingMode) {
auto Op = IROp->C<IR::IROp_SetRoundingMode>();
auto Src = GetReg<RA_32>(Op->Header.Args[0].ID());
// Setup the rounding flags correctly
and_(TMP1, Src, 0b11);
cmp(TMP1, IR::ROUND_MODE_POSITIVE_INFINITY);
LoadConstant(TMP3, 1);
csel(TMP2, TMP3, xzr, vixl::aarch64::Condition::eq);
cmp(TMP1, IR::ROUND_MODE_NEGATIVE_INFINITY);
LoadConstant(TMP3, 2);
csel(TMP2, TMP3, TMP2, vixl::aarch64::Condition::eq);
cmp(TMP1, IR::ROUND_MODE_TOWARDS_ZERO);
LoadConstant(TMP3, 3);
csel(TMP2, TMP3, TMP2, vixl::aarch64::Condition::eq);
mrs(TMP1, FPCR);
// Insert the rounding flags
bfi(TMP1, TMP2, 22, 2);
// Insert the FTZ flag
lsr(TMP2, Src, 2);
bfi(TMP1, TMP2, 24, 1);
// Now save the new FPCR
msr(FPCR, TMP1);
}
#undef DEF_OP
void JITCore::RegisterMiscHandlers() {
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &JITCore::Op_##x
@@ -65,6 +124,8 @@ void JITCore::RegisterMiscHandlers() {
REGISTER_OP(PHI, NoOp);
REGISTER_OP(PHIVALUE, NoOp);
REGISTER_OP(PRINT, Unhandled);
REGISTER_OP(GETROUNDINGMODE, GetRoundingMode);
REGISTER_OP(SETROUNDINGMODE, SetRoundingMode);
#undef REGISTER_OP
}
}
@@ -3455,6 +3455,19 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> const
}
break;
}
case IR::OP_VECTOR_FTOS: {
auto Op = IROp->C<IR::IROp_Vector_FToS>();
switch (Op->Header.ElementSize) {
case 4:
cvtps2dq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
break;
case 8:
cvtpd2dq(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
break;
default: LogMan::Msg::A("Unknown castGPR element size: %d", Op->Header.ElementSize);
}
break;
}
case IR::OP_VECTOR_FTOF: {
auto Op = IROp->C<IR::IROp_Vector_FToF>();
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
@@ -4145,6 +4158,15 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> const
cvttss2si(GetDst<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()));
break;
}
case IR::OP_FLOAT_TOGPR_S: {
auto Op = IROp->C<IR::IROp_Float_ToGPR_S>();
if (Op->Header.ElementSize == 8) {
cvtsd2si(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()));
}
else
cvtss2si(GetDst<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()));
break;
}
case IR::OP_FLOAT_FTOF: {
auto Op = IROp->C<IR::IROp_Float_FToF>();
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
@@ -4910,6 +4932,39 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView<true> const
break;
}
case IR::OP_GETROUNDINGMODE: {
auto Dst = GetDst<RA_32>(Node);
sub(rsp, 4);
// Only stores to memory
stmxcsr(dword [rsp]);
mov(Dst, dword [rsp]);
add(rsp, 4);
shr(Dst, 13);
break;
}
case IR::OP_SETROUNDINGMODE: {
auto Op = IROp->C<IR::IROp_SetRoundingMode>();
auto Src = GetSrc<RA_32>(Op->Header.Args[0].ID());
// Load old mxcsr
// Only stores to memory
sub(rsp, 4);
stmxcsr(dword [rsp]);
mov(TMP1.cvt32(), dword [rsp]);
// Insert the new rounding mode
and(TMP1.cvt32(), ~(0b111 << 13));
mov(TMP2.cvt32(), Src);
shl(TMP2.cvt32(), 13);
or(TMP1.cvt32(), TMP2.cvt32());
// Store it to mxcsr
// Only loads from memory
mov(dword [rsp], TMP1.cvt32());
ldmxcsr(dword [rsp]);
add(rsp, 4);
break;
}
case IR::OP_DUMMY:
case IR::OP_BEGINBLOCK:
case IR::OP_ENDBLOCK:
+103 -1
View File
@@ -40,7 +40,13 @@
"static constexpr FEXCore::IR::FenceType Fence_Load {0}",
"static constexpr FEXCore::IR::FenceType Fence_Store {1}",
"static constexpr FEXCore::IR::FenceType Fence_LoadStore {2}"
"static constexpr FEXCore::IR::FenceType Fence_LoadStore {2}",
"constexpr static uint8_t ROUND_MODE_NEAREST = 0",
"constexpr static uint8_t ROUND_MODE_NEGATIVE_INFINITY = 1",
"constexpr static uint8_t ROUND_MODE_POSITIVE_INFINITY = 2",
"constexpr static uint8_t ROUND_MODE_TOWARDS_ZERO = 3",
"constexpr static uint8_t ROUND_MODE_FLUSH_TO_ZERO = 1 << 2"
],
"Ops": {
@@ -261,6 +267,28 @@
]
},
"GetRoundingMode": {
"Desc": ["Gets the current rounding mode options"
],
"OpClass": "Misc",
"HasDest": true,
"DestClass": "GPR",
"FixedDestSize": "4"
},
"SetRoundingMode": {
"Desc": ["Sets the current rounding mode options for the thread"
],
"HasSideEffects": true,
"OpClass": "Misc",
"HasDest": false,
"DestClass": "GPR",
"SSAArgs": "1",
"SSANames": [
"RoundMode"
]
},
"Constant": {
"Desc": ["Generates a 64bit constant inside of a GPR",
"Unsupported to create a constant in FPR"
@@ -1323,6 +1351,42 @@
]
},
"Float_ToGPR_U": {
"Desc": ["Moves the scalar element to a GPR with conversion",
"Converts the 32bit or 64bit float to an unsigned integer",
"Rounding mode determined by host flag's rounding mode"
],
"OpClass": "ALU",
"HasDest": true,
"DestClass": "GPR",
"DestSize": "ElementSize",
"SSAArgs": "1",
"SSANames": [
"Scalar"
],
"Args": [
"uint8_t", "ElementSize"
]
},
"Float_ToGPR_S": {
"Desc": ["Moves the scalar element to a GPR with conversion",
"Converts the 32bit or 64bit float to an signed integer",
"Rounding mode determined by host flag's rounding mode"
],
"OpClass": "ALU",
"HasDest": true,
"DestClass": "GPR",
"DestSize": "ElementSize",
"SSAArgs": "1",
"SSANames": [
"Scalar"
],
"Args": [
"uint8_t", "ElementSize"
]
},
"Float_ToGPR_ZU": {
"Desc": ["Moves the scalar element to a GPR with conversion",
"Converts the 32bit or 64bit float to an unsigned integer rounding towards zero (Truncating)"
@@ -2778,6 +2842,44 @@
]
},
"Vector_FToU": {
"OpClass": "Conv",
"Desc": ["Vector op: Converts float to unsigned integer",
"Rounding mode determined by host rounding mode"
],
"HasDest": true,
"DestClass": "FPR",
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize",
"SSAArgs": "1",
"SSANames": [
"Vector"
],
"HelperArgs": [
"uint8_t", "RegisterSize",
"uint8_t", "ElementSize"
]
},
"Vector_FToS": {
"OpClass": "Conv",
"Desc": ["Vector op: Converts float to signed integer, rounding towards zero",
"Rounding mode determined by host rounding mode"
],
"HasDest": true,
"DestClass": "FPR",
"DestSize": "RegisterSize",
"NumElements": "RegisterSize / ElementSize",
"SSAArgs": "1",
"SSANames": [
"Vector"
],
"HelperArgs": [
"uint8_t", "RegisterSize",
"uint8_t", "ElementSize"
]
},
"Vector_FToZU": {
"OpClass": "Conv",
"Desc": "Vector op: Converts float to unsigned integer, rounding towards zero",