1 /* Private floating point rounding and exceptions handling.  RISC-V version.
2    Copyright (C) 2014-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #ifndef RISCV_FENV_PRIVATE_H
20 #define RISCV_FENV_PRIVATE_H 1
21 
22 #include <fenv.h>
23 #include <fpu_control.h>
24 #include <get-rounding-mode.h>
25 
26 static __always_inline int
riscv_getround(void)27 riscv_getround (void)
28 {
29   return get_rounding_mode ();
30 }
31 
32 static __always_inline void
riscv_setround(int rm)33 riscv_setround (int rm)
34 {
35   asm volatile ("fsrm %z0" : : "rJ" (rm));
36 }
37 
38 static __always_inline int
riscv_getflags(void)39 riscv_getflags (void)
40 {
41   int flags;
42   asm volatile ("frflags %0" : "=r" (flags));
43   return flags;
44 }
45 
46 static __always_inline void
riscv_setflags(int flags)47 riscv_setflags (int flags)
48 {
49   asm volatile ("fsflags %z0" : : "rJ" (flags));
50 }
51 
52 static __always_inline void
libc_feholdexcept_riscv(fenv_t * envp)53 libc_feholdexcept_riscv (fenv_t *envp)
54 {
55   asm volatile ("csrrc %0, fcsr, %1" : "=r" (*envp) : "i" (FE_ALL_EXCEPT));
56 }
57 
58 #define libc_feholdexcept  libc_feholdexcept_riscv
59 #define libc_feholdexceptf libc_feholdexcept_riscv
60 #define libc_feholdexceptl libc_feholdexcept_riscv
61 
62 static __always_inline void
libc_fesetround_riscv(int round)63 libc_fesetround_riscv (int round)
64 {
65   riscv_setround (round);
66 }
67 
68 #define libc_fesetround  libc_fesetround_riscv
69 #define libc_fesetroundf libc_fesetround_riscv
70 #define libc_fesetroundl libc_fesetround_riscv
71 
72 static __always_inline void
libc_feholdexcept_setround_riscv(fenv_t * envp,int round)73 libc_feholdexcept_setround_riscv (fenv_t *envp, int round)
74 {
75   libc_feholdexcept_riscv (envp);
76   libc_fesetround_riscv (round);
77 }
78 
79 #define libc_feholdexcept_setround  libc_feholdexcept_setround_riscv
80 #define libc_feholdexcept_setroundf libc_feholdexcept_setround_riscv
81 #define libc_feholdexcept_setroundl libc_feholdexcept_setround_riscv
82 
83 static __always_inline int
libc_fetestexcept_riscv(int ex)84 libc_fetestexcept_riscv (int ex)
85 {
86   return riscv_getflags () & ex;
87 }
88 
89 #define libc_fetestexcept  libc_fetestexcept_riscv
90 #define libc_fetestexceptf libc_fetestexcept_riscv
91 #define libc_fetestexceptl libc_fetestexcept_riscv
92 
93 static __always_inline void
libc_fesetenv_riscv(const fenv_t * envp)94 libc_fesetenv_riscv (const fenv_t *envp)
95 {
96   long int env = (long int) envp - (long int) FE_DFL_ENV;
97   if (env != 0)
98     env = *envp;
99 
100   _FPU_SETCW (env);
101 }
102 
103 #define libc_fesetenv  libc_fesetenv_riscv
104 #define libc_fesetenvf libc_fesetenv_riscv
105 #define libc_fesetenvl libc_fesetenv_riscv
106 #define libc_feresetround_noex  libc_fesetenv_riscv
107 #define libc_feresetround_noexf libc_fesetenv_riscv
108 #define libc_feresetround_noexl libc_fesetenv_riscv
109 
110 static __always_inline int
libc_feupdateenv_test_riscv(const fenv_t * envp,int ex)111 libc_feupdateenv_test_riscv (const fenv_t *envp, int ex)
112 {
113   fenv_t env = *envp;
114   int flags = riscv_getflags ();
115   asm volatile ("csrw fcsr, %z0" : : "rJ" (env | flags));
116   return flags & ex;
117 }
118 
119 #define libc_feupdateenv_test  libc_feupdateenv_test_riscv
120 #define libc_feupdateenv_testf libc_feupdateenv_test_riscv
121 #define libc_feupdateenv_testl libc_feupdateenv_test_riscv
122 
123 static __always_inline void
libc_feupdateenv_riscv(const fenv_t * envp)124 libc_feupdateenv_riscv (const fenv_t *envp)
125 {
126   _FPU_SETCW (*envp | riscv_getflags ());
127 }
128 
129 #define libc_feupdateenv  libc_feupdateenv_riscv
130 #define libc_feupdateenvf libc_feupdateenv_riscv
131 #define libc_feupdateenvl libc_feupdateenv_riscv
132 
133 static __always_inline void
libc_feholdsetround_riscv(fenv_t * envp,int round)134 libc_feholdsetround_riscv (fenv_t *envp, int round)
135 {
136   /* Note this implementation makes an improperly-formatted fenv_t and
137      so should only be used in conjunction with libc_feresetround.  */
138   int old_round;
139   asm volatile ("csrrw %0, frm, %z1" : "=r" (old_round) : "rJ" (round));
140   *envp = old_round;
141 }
142 
143 #define libc_feholdsetround  libc_feholdsetround_riscv
144 #define libc_feholdsetroundf libc_feholdsetround_riscv
145 #define libc_feholdsetroundl libc_feholdsetround_riscv
146 
147 static __always_inline void
libc_feresetround_riscv(fenv_t * envp)148 libc_feresetround_riscv (fenv_t *envp)
149 {
150   /* Note this implementation takes an improperly-formatted fenv_t and
151      so should only be used in conjunction with libc_feholdsetround.  */
152   riscv_setround (*envp);
153 }
154 
155 #define libc_feresetround  libc_feresetround_riscv
156 #define libc_feresetroundf libc_feresetround_riscv
157 #define libc_feresetroundl libc_feresetround_riscv
158 
159 #include_next <fenv_private.h>
160 
161 #endif
162