1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <fenv.h>
6
7#include <zircon/compiler.h>
8#include <stdint.h>
9#include <x86intrin.h>
10
11#define ROUND_MASK (FE_DOWNWARD | FE_UPWARD | FE_TOWARDZERO)
12_Static_assert((ROUND_MASK << 3) == _MM_ROUND_MASK, "");
13
14static inline uint16_t get_x87_sw(void) {
15    uint16_t sw;
16    __asm__("fnstsw %0" : "=a"(sw));
17    return sw;
18}
19
20static inline uint16_t get_x87_cw(void) {
21    uint16_t cw;
22    __asm__("fnstcw %0" : "=m"(cw));
23    return cw;
24}
25
26static inline void set_x87_cw(uint16_t cw) {
27    __asm__("fldcw %0" :: "m"(cw));
28}
29
30int fegetround(void) {
31    return _MM_GET_ROUNDING_MODE() >> 3;
32}
33
34__LOCAL int __fesetround(int round) {
35    set_x87_cw((get_x87_cw() & ~ROUND_MASK) | round);
36    _MM_SET_ROUNDING_MODE(round << 3);
37    return 0;
38}
39
40int feclearexcept(int mask) {
41    uint16_t sw = get_x87_sw();
42    if (sw & mask & FE_ALL_EXCEPT)
43        __asm__("fnclex");
44    uint32_t mxcsr = _mm_getcsr();
45    mxcsr |= sw & FE_ALL_EXCEPT;
46    if (mxcsr & mask & FE_ALL_EXCEPT)
47        _mm_setcsr(mxcsr & ~(mask & FE_ALL_EXCEPT));
48    return 0;
49}
50
51int feraiseexcept(int mask) {
52    _mm_setcsr(_mm_getcsr() | (mask & FE_ALL_EXCEPT));
53    return 0;
54}
55
56int fetestexcept(int mask) {
57    return (_mm_getcsr() | get_x87_sw()) & mask & FE_ALL_EXCEPT;
58}
59
60int fegetenv(fenv_t* env) {
61    __asm__("fnstenv %0\n"
62            "stmxcsr %1" : "=m"(*env), "=m"(env->__mxcsr));
63    return 0;
64}
65
66static inline void install_fenv(const fenv_t* env) {
67    __asm__("fldenv %0\n"
68            "ldmxcsr %1"
69            :: "m"(*env), "m"(env->__mxcsr));
70}
71
72int fesetenv(const fenv_t* env) {
73    install_fenv(likely(env != FE_DFL_ENV) ? env :
74                 &(fenv_t){
75                     .__control_word = 0x37f,
76                     .__tags = 0xffff,
77                     .__mxcsr = 0x1f80,
78                  });
79    return 0;
80}
81