#include "rounding_mode.h"
#if (defined(__arm__) || defined(__aarch64__))
#define FPSCR_FZ (1 << 24) // Flush-To-Zero mode
#define FPSCR_ROUND_MASK (3 << 22) // Rounding mode:
#define _ARM_FE_FTZ 0x1000000
#define _ARM_FE_NFTZ 0x0
#if defined(__aarch64__)
#define _FPU_GETCW(cw) __asm__("MRS %0,FPCR" : "=r"(cw))
#define _FPU_SETCW(cw) __asm__("MSR FPCR,%0" : : "ri"(cw))
#else
#define _FPU_GETCW(cw) __asm__("VMRS %0,FPSCR" : "=r"(cw))
#define _FPU_SETCW(cw) __asm__("VMSR FPSCR,%0" : : "ri"(cw))
#endif
#endif
#if (defined(__arm__) || defined(__aarch64__)) && defined(__GNUC__)
#define _ARM_FE_TONEAREST 0x0
#define _ARM_FE_UPWARD 0x400000
#define _ARM_FE_DOWNWARD 0x800000
#define _ARM_FE_TOWARDZERO 0xc00000
RoundingMode set_round(RoundingMode r, Type outType)
{
static const int flt_rounds[kRoundingModeCount] = {
_ARM_FE_TONEAREST, _ARM_FE_TONEAREST, _ARM_FE_UPWARD, _ARM_FE_DOWNWARD,
_ARM_FE_TOWARDZERO
};
static const int int_rounds[kRoundingModeCount] = {
_ARM_FE_TOWARDZERO, _ARM_FE_TONEAREST, _ARM_FE_UPWARD, _ARM_FE_DOWNWARD,
_ARM_FE_TOWARDZERO
};
const int *p = int_rounds;
if (outType == kfloat || outType == kdouble) p = flt_rounds;
int fpscr = 0;
RoundingMode oldRound = get_round();
_FPU_GETCW(fpscr);
_FPU_SETCW(p[r] | (fpscr & ~FPSCR_ROUND_MASK));
return oldRound;
}
RoundingMode get_round(void)
{
int fpscr;
int oldRound;
_FPU_GETCW(fpscr);
oldRound = (fpscr & FPSCR_ROUND_MASK);
switch (oldRound)
{
case _ARM_FE_TONEAREST: return kRoundToNearestEven;
case _ARM_FE_UPWARD: return kRoundUp;
case _ARM_FE_DOWNWARD: return kRoundDown;
case _ARM_FE_TOWARDZERO: return kRoundTowardZero;
}
return kDefaultRoundingMode;
}
#elif !(defined(_WIN32) && defined(_MSC_VER))
RoundingMode set_round(RoundingMode r, Type outType)
{
static const int flt_rounds[kRoundingModeCount] = {
FE_TONEAREST, FE_TONEAREST, FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO
};
static const int int_rounds[kRoundingModeCount] = {
FE_TOWARDZERO, FE_TONEAREST, FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO
};
const int *p = int_rounds;
if (outType == kfloat || outType == kdouble) p = flt_rounds;
int oldRound = fegetround();
fesetround(p[r]);
switch (oldRound)
{
case FE_TONEAREST: return kRoundToNearestEven;
case FE_UPWARD: return kRoundUp;
case FE_DOWNWARD: return kRoundDown;
case FE_TOWARDZERO: return kRoundTowardZero;
default: abort();
}
return kDefaultRoundingMode;
}
RoundingMode get_round(void)
{
int oldRound = fegetround();
switch (oldRound)
{
case FE_TONEAREST: return kRoundToNearestEven;
case FE_UPWARD: return kRoundUp;
case FE_DOWNWARD: return kRoundDown;
case FE_TOWARDZERO: return kRoundTowardZero;
}
return kDefaultRoundingMode;
}
#else
RoundingMode set_round(RoundingMode r, Type outType)
{
static const int flt_rounds[kRoundingModeCount] = { _RC_NEAR, _RC_NEAR,
_RC_UP, _RC_DOWN,
_RC_CHOP };
static const int int_rounds[kRoundingModeCount] = { _RC_CHOP, _RC_NEAR,
_RC_UP, _RC_DOWN,
_RC_CHOP };
const int *p =
(outType == kfloat || outType == kdouble) ? flt_rounds : int_rounds;
unsigned int oldRound;
int err = _controlfp_s(&oldRound, 0, 0);
if (err)
{
vlog_error("\t\tERROR: -- cannot get rounding mode in %s:%d\n",
__FILE__, __LINE__);
return kDefaultRoundingMode;
}
oldRound &= _MCW_RC;
RoundingMode old = (oldRound == _RC_NEAR)
? kRoundToNearestEven
: (oldRound == _RC_UP) ? kRoundUp
: (oldRound == _RC_DOWN)
? kRoundDown
: (oldRound == _RC_CHOP) ? kRoundTowardZero
: kDefaultRoundingMode;
_controlfp_s(&oldRound, p[r], _MCW_RC);
return old;
}
RoundingMode get_round(void)
{
unsigned int oldRound;
int err = _controlfp_s(&oldRound, 0, 0);
oldRound &= _MCW_RC;
return (oldRound == _RC_NEAR)
? kRoundToNearestEven
: (oldRound == _RC_UP) ? kRoundUp
: (oldRound == _RC_DOWN)
? kRoundDown
: (oldRound == _RC_CHOP) ? kRoundTowardZero
: kDefaultRoundingMode;
}
#endif
#if defined(__i386__) || defined(__x86_64__) || defined(_WIN32)
#include <xmmintrin.h>
#elif defined(__PPC__)
#include <fpu_control.h>
#endif
void *FlushToZero(void)
{
#if defined(__APPLE__) || defined(__linux__) || defined(_WIN32)
#if defined(__i386__) || defined(__x86_64__) || defined(_MSC_VER)
union {
int i;
void *p;
} u = { _mm_getcsr() };
_mm_setcsr(u.i | 0x8040);
return u.p;
#elif defined(__arm__) || defined(__aarch64__)
int fpscr;
_FPU_GETCW(fpscr);
_FPU_SETCW(fpscr | FPSCR_FZ);
return NULL;
#elif defined(__PPC__)
fpu_control_t flags = 0;
_FPU_GETCW(flags);
flags |= _FPU_MASK_NI;
_FPU_SETCW(flags);
return NULL;
#else
#error Unknown arch
#endif
#else
#error Please configure FlushToZero and UnFlushToZero to behave properly on this operating system.
#endif
}
void UnFlushToZero(void *p)
{
#if defined(__APPLE__) || defined(__linux__) || defined(_WIN32)
#if defined(__i386__) || defined(__x86_64__) || defined(_MSC_VER)
union {
void *p;
int i;
} u = { p };
_mm_setcsr(u.i);
#elif defined(__arm__) || defined(__aarch64__)
int fpscr;
_FPU_GETCW(fpscr);
_FPU_SETCW(fpscr & ~FPSCR_FZ);
#elif defined(__PPC__)
fpu_control_t flags = 0;
_FPU_GETCW(flags);
flags &= ~_FPU_MASK_NI;
_FPU_SETCW(flags);
#else
#error Unknown arch
#endif
#else
#error Please configure FlushToZero and UnFlushToZero to behave properly on this operating system.
#endif
}