1/*
2 * Copyright 2013, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18#include <portability.h>
19
20#include <sys/cdefs.h>
21#include <sys/types.h>
22#include <fenv.h>
23#include <fenv_portable.h>
24
25static inline int x86_change_except(int flags)
26{
27    int x86flags = 0;
28    int exception = flags & FE_ALL_EXCEPT_PORTABLE;
29
30    // exception flags
31    if (exception & FE_INVALID_PORTABLE)
32        x86flags |= FE_INVALID;
33    if (exception & FE_DIVBYZERO_PORTABLE)
34        x86flags |= FE_DIVBYZERO;
35    if (exception & FE_OVERFLOW_PORTABLE)
36        x86flags |= FE_OVERFLOW;
37    if (exception & FE_UNDERFLOW_PORTABLE)
38        x86flags |= FE_UNDERFLOW;
39    if (exception & FE_INEXACT_PORTABLE)
40        x86flags |= FE_INEXACT;
41
42    return x86flags;
43}
44
45static inline int x86_change_rounding(int flags)
46{
47    int x86flags = 0;
48    int rounding = flags & 0x03;
49
50    // rounding flags
51    switch(rounding)
52    {
53        case FE_TONEAREST_PORTABLE:
54            x86flags = FE_TONEAREST;
55            break;
56        case FE_DOWNWARD_PORTABLE:
57            x86flags = FE_DOWNWARD;
58            break;
59        case FE_UPWARD_PORTABLE:
60            x86flags = FE_UPWARD;
61            break;
62        case FE_TOWARDZERO_PORTABLE:
63            x86flags = FE_TOWARDZERO;
64            break;
65    }
66    return x86flags;
67}
68
69static inline int x86_get_except(int x86flags)
70{
71    int flags = 0;
72    int exception = x86flags & FE_ALL_EXCEPT;
73
74    // exception flags
75    if (exception & FE_INVALID)
76        flags |= FE_INVALID_PORTABLE;
77    if (exception & FE_DIVBYZERO)
78        flags |= FE_DIVBYZERO_PORTABLE;
79    if (exception & FE_OVERFLOW)
80        flags |= FE_OVERFLOW_PORTABLE;
81    if (exception & FE_UNDERFLOW)
82        flags |= FE_UNDERFLOW_PORTABLE;
83    if (exception & FE_INEXACT)
84        flags |= FE_INEXACT_PORTABLE;
85
86    return flags;
87}
88static inline int x86_get_rounding(int x86flags)
89{
90    int flags = 0;
91    int rounding = x86flags & _ROUND_MASK;
92
93    // rounding flags
94    switch(rounding)
95    {
96        case FE_TONEAREST:
97            flags = FE_TONEAREST_PORTABLE;
98            break;
99        case FE_DOWNWARD:
100            flags = FE_DOWNWARD_PORTABLE;
101            break;
102        case FE_UPWARD:
103            flags = FE_UPWARD_PORTABLE;
104            break;
105        case FE_TOWARDZERO:
106            flags = FE_TOWARDZERO_PORTABLE;
107            break;
108    }
109
110    return flags;
111}
112
113int
114WRAP(fesetexceptflag)(const fexcept_t *flagp, int excepts)
115{
116    const fexcept_t flagp_ = x86_change_except(*flagp);
117    int excepts_ = x86_change_except(excepts);
118    return REAL(fesetexceptflag)(&flagp_, excepts_);
119}
120
121int
122WRAP(fegetexceptflag)(fexcept_t *flagp, int excepts)
123{
124    REAL(fegetexceptflag)(flagp, x86_change_except(excepts));
125    *flagp = x86_get_except(*flagp);
126    return 0;
127}
128
129int
130WRAP(feraiseexcept)(int excepts)
131{
132    return REAL(feraiseexcept)(x86_change_except(excepts));
133}
134
135int
136WRAP(feclearexcept)(int excepts)
137{
138    return REAL(feclearexcept)(x86_change_except(excepts));
139}
140
141int
142WRAP(fetestexcept)(int excepts)
143{
144    return REAL(fetestexcept)(x86_change_except(excepts));
145}
146
147int
148WRAP(fegetround)(void)
149{
150    int round = REAL(fegetround)();
151    return x86_get_rounding(round);
152}
153
154int
155WRAP(fesetround)(int round)
156{
157    return REAL(fesetround)(x86_change_rounding(round));
158}
159
160int
161WRAP(fegetexcept)(void)
162{
163    int flags = REAL(fegetexcept)();
164    return x86_get_except(flags);
165}
166
167