1 /*
2   (c) Copyright 2017 Michael R. Neilly
3   All rights reserved.
4 
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions
7   are met:
8 
9   * Redistributions of source code must retain the above copyright
10   notice, this list of conditions and the following disclaimer.
11 
12   * Redistributions in binary form must reproduce the above copyright
13   notice, this list of conditions and the following disclaimer in the
14   documentation and/or other materials provided with the distribution.
15 
16   * Neither the names of the copyright holders nor the names of their
17   contributors may be used to endorse or promote products derived from
18   this software without specific prior written permission.
19 
20   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24   COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31   POSSIBILITY OF SUCH DAMAGE.
32 */
33 
34 /* This implementation is intended to comply with the following
35  * specification:
36  *
37  * http://pubs.opengroup.org/onlinepubs/009695399/functions/feclearexcept.html
38  * Referred to as 'feclearexcept.html below.
39  *
40  * "The feclearexcept() function shall attempt to clear the supported
41  * floating-point exceptions represented by excepts."
42  */
43 
feclearexcept(int excepts)44 __declare_fenv_inline(int) feclearexcept(int excepts)
45 {
46 
47   /* Mask excepts to be sure only supported flag bits are set */
48 
49   excepts &= FE_ALL_EXCEPT;
50 
51   /* Per "The RISC-V Instruction Set Manual: Volume I: User-Level ISA:
52    * Version 2.1":
53    *
54    * "The CSRRC (Atomic Read and Clear Bits in CSR) instruction reads
55    * the value of the CSR, zeroextends the value to XLEN bits, and
56    * writes it to integer register rd. The initial value in integer
57    * register rs1 is treated as a bit mask that specifies bit
58    * positions to be cleared in the CSR. Any bit that is high in rs1
59    * will cause the corresponding bit to be cleared in the CSR, if
60    * that CSR bit is writable. Other bits in the CSR are unaffected."
61    */
62 
63   /* Clear the requested flags */
64 
65   __asm__ volatile("csrrc zero, fflags, %0" : : "r"(excepts));
66 
67   /* Per 'feclearexcept.html
68    * "If the argument is zero or if all the specified exceptions were
69    * successfully cleared, feclearexcept() shall return zero. Otherwise,
70    * it shall return a non-zero value."
71    */
72 
73   return 0;
74 }
75 
76 /* This implementation is intended to comply with the following
77  * specification:
78  *
79  * http://pubs.opengroup.org/onlinepubs/009695399/functions/fegetenv.html
80  * Referred to as 'fegetenv.html below.
81  *
82  * "The fegetenv() function shall attempt to store the current
83  * floating-point environment in the object pointed to by envp."
84  *
85  */
86 
fegetenv(fenv_t * envp)87 __declare_fenv_inline(int) fegetenv(fenv_t *envp)
88 {
89 
90   /* Get the current environment (FCSR) */
91 
92   fenv_t fcsr;
93   __asm__ volatile("frcsr %0" : "=r"(fcsr));
94 
95   /* Store FCSR in envp */
96 
97   *envp = fcsr;
98 
99   /* Per 'fegetenv.html:
100    *
101    * "If the representation was successfully stored, fegetenv() shall
102    * return zero. Otherwise, it shall return a non-zero value.
103    */
104 
105   return 0;
106 }
107 
108 /* This implementation is intended to comply with the following
109  * specification:
110  *
111  * http://pubs.opengroup.org/onlinepubs/009695399/functions/fegetexceptflag.html
112  * Referred to as 'fegetexceptflag.html below.
113  *
114  * "The fegetexceptflag() function shall attempt to store an
115  * implementation-defined representation of the states of the
116  * floating-point status flags indicated by the argument excepts in
117  * the object pointed to by the argument flagp."
118  */
119 
fegetexceptflag(fexcept_t * flagp,int excepts)120 __declare_fenv_inline(int) fegetexceptflag(fexcept_t *flagp, int excepts)
121 {
122 
123   /* Mask excepts to be sure only supported flag bits are set */
124 
125   excepts &= FE_ALL_EXCEPT;
126 
127   /* Get current exception flags */
128 
129   fexcept_t flags;
130   __asm__ volatile("frflags %0" : "=r"(flags));
131 
132   /* Return the requested flags in flagp */
133 
134   *flagp = flags & excepts;
135 
136   /* Per 'fegetexceptflag.html:
137    *
138    * "If the representation was successfully stored, fegetexceptflag()
139    * shall return zero. Otherwise, it shall return a non-zero
140    * value."
141    */
142 
143   return 0;
144 }
145 
146 /* This implementation is intended to comply with the following
147  * specification:
148  *
149  * http://pubs.opengroup.org/onlinepubs/009695399/functions/fegetround.html
150  * Referred to as 'fegetround.html below.
151  *
152  * "The fegetround() function shall get the current rounding direction."
153  */
154 
fegetround(void)155 __declare_fenv_inline(int) fegetround(void)
156 {
157 
158   /* Get current rounding mode */
159 
160   fenv_t frm;
161   __asm__ volatile("frrm %0" : "=r"(frm));
162 
163   /* Per 'fegetround.html:
164    *
165    * "The fegetround() function shall return the value of the rounding
166    * direction macro representing the current rounding direction or a
167    * negative value if there is no such rounding direction macro or
168    * the current rounding direction is not determinable."
169    */
170 
171   /* Return the rounding mode */
172 
173   return frm;
174 
175 }
176 
177 /* This implementation is intended to comply with the following
178  * specification:
179  *
180  * http://pubs.opengroup.org/onlinepubs/009695399/functions/feholdexcept.html
181  * Referred to as 'feholdexcept.html below.
182  *
183  * "The feholdexcept() function shall save the current floating-point
184  * environment in the object pointed to by envp, clear the
185  * floating-point status flags, and then install a non-stop (continue
186  * on floating-point exceptions) mode, if available, for all
187  * floating-point exceptions."
188  */
189 
feholdexcept(fenv_t * envp)190 __declare_fenv_inline(int) feholdexcept(fenv_t *envp)
191 {
192 
193   /* Store the current FP environment in envp*/
194 
195   fenv_t fcsr;
196   __asm__ volatile("frcsr %0" : "=r"(fcsr));
197   *envp = fcsr;
198 
199   /* Clear all flags */
200 
201   __asm__ volatile("csrrc zero, fflags, %0" : : "r"(FE_ALL_EXCEPT));
202 
203   /* RISC-V does not raise FP traps so it is always in a "non-stop" mode */
204 
205   /* Per 'feholdexcept.html:
206    *
207    * "The feholdexcept() function shall return zero if and only if
208    * non-stop floating-point exception handling was successfully
209    * installed."
210    */
211 
212   return 0;
213 }
214 
215 /* This implementation is intended to comply with the following
216  * specification:
217  *
218  * http://pubs.opengroup.org/onlinepubs/009695399/functions/feraiseexcept.html
219  * Referred to as 'feraiseexcept.html below.
220  *
221  * "The feraiseexcept() function shall attempt to raise the supported
222  * floating-point exceptions represented by the excepts argument. The
223  * order in which these floating-point exceptions are raised is
224  * unspecified, except that if the excepts argument represents IEC
225  * 60559 valid coincident floating-point exceptions for atomic
226  * operations (namely overflow and inexact, or underflow and inexact),
227  * then overflow or underflow shall be raised before inexact. Whether
228  * the feraiseexcept() function additionally raises the inexact
229  * floating-point exception whenever it raises the overflow or
230  * underflow floating-point exception is implementation-defined."
231  */
232 
feraiseexcept(int excepts)233 __declare_fenv_inline(int) feraiseexcept(int excepts)
234 {
235 
236   /* Mask excepts to be sure only supported flag bits are set */
237 
238   excepts &= FE_ALL_EXCEPT;
239 
240   /* Set the requested exception flags */
241 
242   __asm__ volatile("csrs fflags, %0" : : "r"(excepts));
243 
244   /* Per 'feraiseexcept.html:
245    * "If the argument is zero or if all the specified exceptions were
246    * successfully raised, feraiseexcept() shall return
247    * zero. Otherwise, it shall return a non-zero value."
248    */
249 
250   /* Per "The RISC-V Instruction Set Manual: Volume I: User-Level ISA:
251    * Version 2.1":
252    *
253    * "As allowed by the standard, we do not support traps on
254    * floating-point exceptions in the base ISA, but instead require
255    * explicit checks of the flags in software."
256    *
257    */
258 
259   return 0;
260 }
261 
fesetexcept(int excepts)262 __declare_fenv_inline(int) fesetexcept(int excepts)
263 {
264     return feraiseexcept(excepts);
265 }
266 
267 /* This implementation is intended to comply with the following
268  * specification:
269  *
270  * http://pubs.opengroup.org/onlinepubs/009695399/functions/fegetenv.html
271  * Referred to as 'fegetenv.html below.
272  *
273  * "The fegetenv() function shall attempt to store the current
274  * floating-point environment in the object pointed to by envp.
275  *
276  * The fesetenv() function shall attempt to establish the
277  * floating-point environment represented by the object pointed to by
278  * envp. The argument envp shall point to an object set by a call to
279  * fegetenv() or feholdexcept(), or equal a floating-point environment
280  * macro. The fesetenv() function does not raise floating-point
281  * exceptions, but only installs the state of the floating-point
282  * status flags represented through its argument."
283  */
284 
fesetenv(const fenv_t * envp)285 __declare_fenv_inline(int) fesetenv(const fenv_t *envp)
286 {
287 
288   /* Set environment (FCSR) */
289 
290   fenv_t fcsr = *envp;
291   __asm__ volatile("fscsr %0" : : "r"(fcsr));
292 
293   /* Per 'fegetenv.html:
294    *
295    * "If the environment was successfully established, fesetenv()
296    * shall return zero. Otherwise, it shall return a non-zero value.
297    */
298 
299   return 0;
300 }
301 
302 /* This implementation is intended to comply with the following
303  * specification:
304  *
305  * http://pubs.opengroup.org/onlinepubs/009695399/functions/fesetexceptflag.html
306  * Referred to as 'fesetexceptflag.html below.
307  *
308  * "The fesetexceptflag() function shall attempt to set the
309  * floating-point status flags indicated by the argument excepts to
310  * the states stored in the object pointed to by flagp. The value
311  * pointed to by flagp shall have been set by a previous call to
312  * fegetexceptflag() whose second argument represented at least those
313  * floating-point exceptions represented by the argument excepts. This
314  * function does not raise floating-point exceptions, but only sets
315  * the state of the flags."
316  *
317  */
318 
fesetexceptflag(const fexcept_t * flagp,int excepts)319 __declare_fenv_inline(int) fesetexceptflag(const fexcept_t *flagp, int excepts)
320 {
321 
322   /* Mask excepts to be sure only supported flag bits are set */
323 
324   excepts &= FE_ALL_EXCEPT;
325 
326   /* Set the requested flags */
327 
328   fexcept_t flags = *flagp & FE_ALL_EXCEPT;
329 
330   /* Set new the flags */
331 
332   __asm__ volatile("csrc fflags, %0" : : "r"(excepts));
333   __asm__ volatile("csrs fflags, %0" : : "r"(flags & excepts));
334 
335   /* Per 'fesetexceptflag.html:
336    *
337    * "If the excepts argument is zero or if all the specified
338    * exceptions were successfully set, fesetexceptflag() shall return
339    * zero. Otherwise, it shall return a non-zero value."
340    */
341 
342   return 0;
343 }
344 
345 /* This implementation is intended to comply with the following
346  * specification:
347  *
348  * http://pubs.opengroup.org/onlinepubs/009695399/functions/fesetround.html
349  * Referred to as 'fegetenv.html below.
350  *
351  * "The fesetround() function shall establish the rounding direction
352  * represented by its argument round. If the argument is not equal to
353  * the value of a rounding direction macro, the rounding direction is
354  * not changed."
355  */
356 
fesetround(int round)357 __declare_fenv_inline(int) fesetround(int round)
358 {
359 
360   /* Mask round to be sure only valid rounding bits are set */
361 
362   round &= FE_RMODE_MASK;
363 
364   /* Set the rounding mode */
365 
366   __asm__ volatile("fsrm %0" : : "r"(round));
367 
368   /* Per 'fesetround.html:
369    *
370    * "The fesetround() function shall return a zero value if and only
371    * if the requested rounding direction was established."
372    */
373 
374   return 0;
375 }
376 
377 /* This implementation is intended to comply with the following
378  * specification:
379  *
380  * http://pubs.opengroup.org/onlinepubs/9699919799/functions/fetestexcept.html
381  *
382  * "The fetestexcept() function shall determine which of a specified
383  * subset of the floating-point exception flags are currently set. The
384  * excepts argument specifies the floating-point status flags to be
385  * queried."
386  */
387 
fetestexcept(int excepts)388 __declare_fenv_inline(int) fetestexcept(int excepts)
389 {
390 
391   /* Mask excepts to be sure only supported flag bits are set */
392 
393   excepts &= FE_ALL_EXCEPT;
394 
395   /* Read the current flags */
396 
397   fexcept_t flags;
398   __asm__ volatile("frflags %0" : "=r"(flags));
399 
400   /* "The fetestexcept() function shall return the value of the
401    * bitwise-inclusive OR of the floating-point exception macros
402    * corresponding to the currently set floating-point exceptions
403    * included in excepts."
404    */
405 
406   return (flags & excepts);
407 }
408 
409 /* This implementation is intended to comply with the following
410  * specification:
411  *
412  * http://pubs.opengroup.org/onlinepubs/9699919799/functions/feupdateenv.html
413  *
414  * "The feupdateenv() function shall attempt to save the currently
415  * raised floating-point exceptions in its automatic storage, attempt
416  * to install the floating-point environment represented by the object
417  * pointed to by envp, and then attempt to raise the saved
418  * floating-point exceptions. The argument envp shall point to an
419  * object set by a call to feholdexcept() or fegetenv(), or equal a
420  * floating-point environment macro."
421  */
422 
feupdateenv(const fenv_t * envp)423 __declare_fenv_inline(int) feupdateenv(const fenv_t *envp)
424 {
425 
426   /* Get current exception flags */
427 
428   fexcept_t flags;
429   __asm__ volatile("frflags %0" : "=r"(flags));
430 
431   /* Set the environment as requested */
432 
433   fenv_t fcsr = *envp; /* Environment to install */
434   __asm__ volatile("fscsr %0" : : "r"(fcsr)); /* Install environment */
435 
436   /* OR in the saved exception flags */
437 
438   __asm__ volatile("csrs fflags, %0" : : "r"(flags));
439 
440   /* "The feupdateenv() function shall return a zero value if and only
441    * if all the required actions were successfully carried out."
442    */
443 
444   return 0;
445 
446 }
447 
feenableexcept(int excepts)448 __declare_fenv_inline(int) feenableexcept(int excepts)
449 {
450     return excepts == 0 ? 0 : -1;
451 }
452 
fedisableexcept(int excepts)453 __declare_fenv_inline(int) fedisableexcept(int excepts)
454 {
455     (void) excepts;
456     return excepts == 0 ? 0 : -1;
457 }
458 
fegetexcept(void)459 __declare_fenv_inline(int) fegetexcept(void)
460 {
461     return 0;
462 }
463