blob: 3a68306dabd7093282bba0f74b04d3334546083e [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 <stdint.h>
#include <x86intrin.h>
#include <zircon/compiler.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;
}