Files
FEX-Emu--FEX/External/FEXCore/Source/Common/SoftFloat.h
T
Ryan Houdek 3759cbdb65 Interpreter based x87 support!
This implements support for 130 of the 132 x87 ops as interpreter
fallbacks.

The two missing ops are the BCD load and BCD store instructions and can
be implemented another time.

This allows us to no longer have to rely on a libstdc++ modification to
handle their usage of long double. This instead works entirely through
using `long double` directly in the interpreter. Which will either use
real x87 on an x86 host. Or in the case of ARM devices, fall down
glibc's long double soft float path.

This doesn't necessarily need to be quick right now. It's more important
to have the compatibility improvement from this.
2020-05-28 14:47:43 -07:00

256 lines
6.4 KiB
C++

#pragma once
#include "LogManager.h"
#include <cmath>
#include <cstring>
#include <stdint.h>
#include <string>
#include <sstream>
extern "C" {
#include "SoftFloat-3e/platform.h"
#include "SoftFloat-3e/softfloat.h"
}
struct X80SoftFloat {
#ifdef _M_X86_64
#define BIGFLOAT __float128
#elif defined(_M_ARM_64)
#define BIGFLOAT long double
#else
#error No 128bit float for this target!
#endif
struct __attribute__((packed)) {
uint64_t Significand : 64;
uint16_t Exponent : 15;
unsigned Sign : 1;
};
X80SoftFloat() { memset(this, 0, sizeof(*this)); }
X80SoftFloat(unsigned _Sign, uint16_t _Exponent, uint64_t _Significand)
: Significand {_Significand}
, Exponent {_Exponent}
, Sign {_Sign}
{
}
std::string str() const {
std::ostringstream string;
string << std::hex << Sign;
string << "_" << Exponent;
string << "_" << (Significand >> 63);
string << "_" << (Significand & ((1ULL << 63) - 1));
return string.str();
}
// Ops
static X80SoftFloat FADD(X80SoftFloat const &lhs, X80SoftFloat const &rhs) {
return extF80_add(lhs, rhs);
}
static X80SoftFloat FSUB(X80SoftFloat const &lhs, X80SoftFloat const &rhs) {
return extF80_sub(lhs, rhs);
}
static X80SoftFloat FMUL(X80SoftFloat const &lhs, X80SoftFloat const &rhs) {
return extF80_mul(lhs, rhs);
}
static X80SoftFloat FDIV(X80SoftFloat const &lhs, X80SoftFloat const &rhs) {
return extF80_div(lhs, rhs);
}
static X80SoftFloat FREM(X80SoftFloat const &lhs, X80SoftFloat const &rhs) {
X80SoftFloat Rem = extF80_rem(lhs, rhs);
if (SignBit(Rem)) {
Rem = extF80_add(Rem, rhs);
}
else {
Rem.Sign = SignBit(lhs);
}
return Rem;
}
static X80SoftFloat FREM1(X80SoftFloat const &lhs, X80SoftFloat const &rhs) {
return extF80_rem(lhs, rhs);
}
static X80SoftFloat FRNDINT(X80SoftFloat const &lhs) {
return extF80_roundToInt(lhs, softfloat_round_near_even, false);
}
static X80SoftFloat FXTRACT_SIG(X80SoftFloat const &lhs) {
X80SoftFloat Tmp = lhs;
Tmp.Exponent = 0x3FFF;
Tmp.Sign = lhs.Sign;
return Tmp;
}
static X80SoftFloat FXTRACT_EXP(X80SoftFloat const &lhs) {
int32_t TrueExp = lhs.Exponent - ExponentBias;
return i32_to_extF80(TrueExp);
}
static void FCMP(X80SoftFloat const &lhs, X80SoftFloat const &rhs, bool *eq, bool *lt, bool *nan) {
*eq = extF80_eq(lhs, rhs);
*lt = extF80_lt(lhs, rhs);
*nan = IsNan(lhs) || IsNan(rhs);
}
static X80SoftFloat FSCALE(X80SoftFloat const &lhs, X80SoftFloat const &rhs) {
WARN_ONCE("x87: Application used FSCALE which may have accuracy problems");
X80SoftFloat Int = FRNDINT(rhs);
BIGFLOAT Src2_d = Int;
Src2_d = exp2l(Src2_d);
X80SoftFloat Src2_X80 = Src2_d;
X80SoftFloat Result = extF80_mul(lhs, Src2_X80);
return Result;
}
static X80SoftFloat F2XM1(X80SoftFloat const &lhs) {
WARN_ONCE("x87: Application used F2XM1 which may have accuracy problems");
BIGFLOAT Src1_d = lhs;
BIGFLOAT Result = exp2l(Src1_d);
Result -= 1.0;
return Result;
}
static X80SoftFloat FYL2X(X80SoftFloat const &lhs, X80SoftFloat const &rhs) {
WARN_ONCE("x87: Application used FYL2X which may have accuracy problems");
BIGFLOAT Src1_d = lhs;
BIGFLOAT Src2_d = rhs;
BIGFLOAT Tmp = Src2_d * log2l(Src1_d);
return Tmp;
}
static X80SoftFloat FATAN(X80SoftFloat const &lhs, X80SoftFloat const &rhs) {
WARN_ONCE("x87: Application used FATAN which may have accuracy problems");
BIGFLOAT Src1_d = lhs;
BIGFLOAT Src2_d = rhs;
BIGFLOAT Tmp = atan2l(Src1_d, Src2_d);
return Tmp;
}
static X80SoftFloat FTAN(X80SoftFloat const &lhs) {
WARN_ONCE("x87: Application used FTAN which may have accuracy problems");
BIGFLOAT Src_d = lhs;
Src_d = tanl(Src_d);
return Src_d;
}
static X80SoftFloat FSIN(X80SoftFloat const &lhs) {
WARN_ONCE("x87: Application used FSIN which may have accuracy problems");
BIGFLOAT Src_d = lhs;
Src_d = sinl(Src_d);
return Src_d;
}
static X80SoftFloat FCOS(X80SoftFloat const &lhs) {
WARN_ONCE("x87: Application used FCOS which may have accuracy problems");
BIGFLOAT Src_d = lhs;
Src_d = cosl(Src_d);
return Src_d;
}
static X80SoftFloat FSQRT(X80SoftFloat const &lhs) {
return extF80_sqrt(lhs);
}
operator float() const {
float32_t Result = extF80_to_f32(*this);
return *(float*)&Result;
}
operator double() const {
float64_t Result = extF80_to_f64(*this);
return *(double*)&Result;
}
operator BIGFLOAT() const {
float128_t Result = extF80_to_f128(*this);
return *(BIGFLOAT*)&Result;
}
operator int16_t() const {
return extF80_to_i32(*this, softfloat_round_near_even, false);
}
operator int32_t() const {
return extF80_to_i32(*this, softfloat_round_near_even, false);
}
operator int64_t() const {
return extF80_to_i64(*this, softfloat_round_near_even, false);
}
void operator=(const float rhs) {
*this = f32_to_extF80(*(float32_t*)&rhs);
}
void operator=(const double rhs) {
*this = f64_to_extF80(*(float64_t*)&rhs);
}
void operator=(const int16_t rhs) {
*this = i32_to_extF80(rhs);
}
void operator=(const int32_t rhs) {
*this = i32_to_extF80(rhs);
}
operator void*() {
return reinterpret_cast<void*>(this);
}
X80SoftFloat(extFloat80_t rhs) {
memcpy(this, &rhs, sizeof(*this));
}
X80SoftFloat(const float rhs) {
*this = f32_to_extF80(*(float32_t*)&rhs);
}
X80SoftFloat(const double rhs) {
*this = f64_to_extF80(*(float64_t*)&rhs);
}
X80SoftFloat(BIGFLOAT rhs) {
*this = f128_to_extF80(*(float128_t*)&rhs);
}
X80SoftFloat(const int16_t rhs) {
*this = i32_to_extF80(rhs);
}
X80SoftFloat(const int32_t rhs) {
*this = i32_to_extF80(rhs);
}
void operator=(extFloat80_t rhs) {
memcpy(this, &rhs, sizeof(*this));
}
operator extFloat80_t() const {
return *(extFloat80_t*)this;
}
static bool IsNan(X80SoftFloat const &lhs) {
return (lhs.Exponent == 0x7FFF) &&
(lhs.Significand & IntegerBit) &&
(lhs.Significand & Bottom62Significand);
}
static bool SignBit(X80SoftFloat const &lhs) {
return lhs.Sign;
}
private:
static constexpr uint64_t IntegerBit = (1ULL << 63);
static constexpr uint64_t Bottom62Significand = ((1ULL << 62) - 1);
static constexpr uint32_t ExponentBias = 16383;
};
static_assert(sizeof(X80SoftFloat) == 10, "tword must be 10bytes in size");