blob: 26a82fc1836511dc9e96b6a82ce225948c8b246f [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fenv.h>
#include <magenta/compiler.h>
#include <stdint.h>
#include <x86intrin.h>
#define ROUND_MASK (FE_DOWNWARD | FE_UPWARD | FE_TOWARDZERO)
_Static_assert((ROUND_MASK << 3) == _MM_ROUND_MASK, "");
static inline uint16_t get_x87_sw(void) {
uint16_t sw;
__asm__("fnstsw %0" : "=a"(sw));
return sw;
}
static inline uint16_t get_x87_cw(void) {
uint16_t cw;
__asm__("fnstcw %0" : "=m"(cw));
return cw;
}
static inline void set_x87_cw(uint16_t cw) {
__asm__("fldcw %0" :: "m"(cw));
}
int fegetround(void) {
return _MM_GET_ROUNDING_MODE() >> 3;
}
__LOCAL int __fesetround(int round) {
set_x87_cw((get_x87_cw() & ~ROUND_MASK) | round);
_MM_SET_ROUNDING_MODE(round << 3);
return 0;
}
int feclearexcept(int mask) {
uint16_t sw = get_x87_sw();
if (sw & mask & FE_ALL_EXCEPT)
__asm__("fnclex");
uint32_t mxcsr = _mm_getcsr();
mxcsr |= sw & FE_ALL_EXCEPT;
if (mxcsr & mask & FE_ALL_EXCEPT)
_mm_setcsr(mxcsr & ~(mask & FE_ALL_EXCEPT));
return 0;
}
int feraiseexcept(int mask) {
_mm_setcsr(_mm_getcsr() | (mask & FE_ALL_EXCEPT));
return 0;
}
int fetestexcept(int mask) {
return (_mm_getcsr() | get_x87_sw()) & mask & FE_ALL_EXCEPT;
}
int fegetenv(fenv_t* env) {
__asm__("fnstenv %0\n"
"stmxcsr %1" : "=m"(*env), "=m"(env->__mxcsr));
return 0;
}
static inline void install_fenv(const fenv_t* env) {
__asm__("fldenv %0\n"
"ldmxcsr %1"
:: "m"(*env), "m"(env->__mxcsr));
}
int fesetenv(const fenv_t* env) {
install_fenv(likely(env != FE_DFL_ENV) ? env :
&(fenv_t){
.__control_word = 0x37f,
.__tags = 0xffff,
.__mxcsr = 0x1f80,
});
return 0;
}