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#include <portability.h>
18#include <sys/types.h>
19#include <fenv.h>
20#include <fenv_portable.h>
21
22static inline int mips_change_except(int flags)
23{
24    int mipsflags = 0;
25    int exception = flags & FE_ALL_EXCEPT_PORTABLE;
26
27    // exception flags
28    if (exception & FE_INVALID_PORTABLE)
29        mipsflags |= FE_INVALID;
30    if (exception & FE_DIVBYZERO_PORTABLE)
31        mipsflags |= FE_DIVBYZERO;
32    if (exception & FE_OVERFLOW_PORTABLE)
33        mipsflags |= FE_OVERFLOW;
34    if (exception & FE_UNDERFLOW_PORTABLE)
35        mipsflags |= FE_UNDERFLOW;
36    if (exception & FE_INEXACT_PORTABLE)
37        mipsflags |= FE_INEXACT;
38
39    return mipsflags;
40}
41
42static inline int mips_change_rounding(int flags)
43{
44    int mipsflags = 0;
45    int rounding = flags & 0x03;
46
47    // rounding flags
48    switch(rounding)
49    {
50      case FE_TONEAREST_PORTABLE:
51        mipsflags = FE_TONEAREST;
52        break;
53      case FE_DOWNWARD_PORTABLE:
54        mipsflags = FE_DOWNWARD;
55        break;
56      case FE_UPWARD_PORTABLE:
57        mipsflags = FE_UPWARD;
58        break;
59      case FE_TOWARDZERO_PORTABLE:
60        mipsflags = FE_TOWARDZERO;
61        break;
62    }
63    return mipsflags;
64}
65
66static inline int mips_get_except(int mipsflags)
67{
68    int flags = 0;
69    int exception = mipsflags & FE_ALL_EXCEPT;
70
71    // exception flags
72    if (exception & FE_INVALID)
73        flags |= FE_INVALID_PORTABLE;
74    if (exception & FE_DIVBYZERO)
75        flags |= FE_DIVBYZERO_PORTABLE;
76    if (exception & FE_OVERFLOW)
77        flags |= FE_OVERFLOW_PORTABLE;
78    if (exception & FE_UNDERFLOW)
79        flags |= FE_UNDERFLOW_PORTABLE;
80    if (exception & FE_INEXACT)
81        flags |= FE_INEXACT_PORTABLE;
82    return flags;
83}
84
85static inline int mips_get_rounding(int mipsflags)
86{
87    int flags = 0;
88    int rounding = mipsflags & _FCSR_RMASK;
89
90    // rounding flags
91    switch(rounding)
92    {
93      case FE_TONEAREST:
94        flags = FE_TONEAREST_PORTABLE;
95        break;
96      case FE_DOWNWARD:
97        flags = FE_DOWNWARD_PORTABLE;
98        break;
99      case FE_UPWARD:
100        flags = FE_UPWARD_PORTABLE;
101        break;
102      case FE_TOWARDZERO:
103        flags = FE_TOWARDZERO_PORTABLE;
104        break;
105    }
106    return flags;
107}
108
109int WRAP(fegetenv)(fenv_t* __envp) {
110   fenv_t _fcsr = 0;
111#ifdef  __mips_hard_float
112   __asm__ __volatile__("cfc1 %0,$31" : "=r" (_fcsr));
113#endif
114   *__envp = _fcsr;
115   return 0;
116}
117
118int WRAP(fesetenv)(const fenv_t* __envp) {
119  fenv_t _fcsr = *__envp;
120#ifdef  __mips_hard_float
121  __asm__ __volatile__("ctc1 %0,$31" : : "r" (_fcsr));
122#endif
123  return 0;
124}
125
126int WRAP(feclearexcept)(int __excepts) {
127  __excepts = mips_change_except(__excepts);
128  fexcept_t __fcsr;
129  WRAP(fegetenv)(&__fcsr);
130  __excepts &= FE_ALL_EXCEPT;
131  __fcsr &= ~(__excepts | (__excepts << _FCSR_CAUSE_SHIFT));
132  WRAP(fesetenv)(&__fcsr);
133  return 0;
134}
135
136int WRAP(fegetexceptflag)(fexcept_t* __flagp, int __excepts) {
137  __excepts = mips_change_except(__excepts);
138  fexcept_t __fcsr;
139  WRAP(fegetenv)(&__fcsr);
140  *__flagp = mips_get_except(__fcsr & __excepts & FE_ALL_EXCEPT);
141  return 0;
142}
143
144int WRAP(fesetexceptflag)(const fexcept_t* __flagp, int __excepts) {
145  int __flagp_ = mips_change_except(*__flagp);
146  __excepts = mips_change_except(__excepts);
147  fexcept_t __fcsr;
148  WRAP(fegetenv)(&__fcsr);
149  /* Ensure that flags are all legal */
150  __excepts &= FE_ALL_EXCEPT;
151  __fcsr &= ~__excepts;
152  __fcsr |= __flagp_ & __excepts;
153  WRAP(fesetenv)(&__fcsr);
154  return 0;
155}
156
157int WRAP(feraiseexcept)(int __excepts) {
158  __excepts = mips_change_except(__excepts);
159  fexcept_t __fcsr;
160  WRAP(fegetenv)(&__fcsr);
161  /* Ensure that flags are all legal */
162  __excepts &= FE_ALL_EXCEPT;
163  /* Cause bit needs to be set as well for generating the exception*/
164  __fcsr |= __excepts | (__excepts << _FCSR_CAUSE_SHIFT);
165  WRAP(fesetenv)(&__fcsr);
166  return 0;
167}
168
169int WRAP(fetestexcept)(int __excepts) {
170   __excepts = mips_change_except(__excepts);
171  fexcept_t __FCSR;
172  WRAP(fegetenv)(&__FCSR);
173  return mips_get_except(__FCSR & __excepts & FE_ALL_EXCEPT);
174}
175
176int WRAP(fegetround)(void) {
177  fenv_t _fcsr;
178  WRAP(fegetenv)(&_fcsr);
179  return mips_get_rounding(_fcsr & _FCSR_RMASK);
180}
181
182int WRAP(fesetround)(int __round) {
183  __round = mips_change_rounding(__round);
184  fenv_t _fcsr;
185  WRAP(fegetenv)(&_fcsr);
186  _fcsr &= ~_FCSR_RMASK;
187  _fcsr |= (__round & _FCSR_RMASK );
188  WRAP(fesetenv)(&_fcsr);
189  return 0;
190}
191
192int WRAP(feholdexcept)(fenv_t* __envp) {
193  fenv_t __env;
194  WRAP(fegetenv)(&__env);
195  *__envp = __env;
196  __env &= ~(FE_ALL_EXCEPT | _FCSR_ENABLE_MASK);
197  WRAP(fesetenv)(&__env);
198  return 0;
199}
200
201int WRAP(feupdateenv)(const fenv_t* __envp) {
202  fexcept_t __fcsr;
203  WRAP(fegetenv)(&__fcsr);
204  WRAP(fesetenv)(__envp);
205  WRAP(feraiseexcept)(__fcsr & FE_ALL_EXCEPT);
206  return 0;
207}
208
209