1 /* Private floating point rounding and exceptions handling.  AArch64 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 AARCH64_FENV_PRIVATE_H
20 #define AARCH64_FENV_PRIVATE_H 1
21 
22 #include <fenv.h>
23 #include <fpu_control.h>
24 
25 static __always_inline void
libc_feholdexcept_aarch64(fenv_t * envp)26 libc_feholdexcept_aarch64 (fenv_t *envp)
27 {
28   fpu_control_t fpcr;
29   fpu_control_t new_fpcr;
30   fpu_fpsr_t fpsr;
31   fpu_fpsr_t new_fpsr;
32 
33   _FPU_GETCW (fpcr);
34   _FPU_GETFPSR (fpsr);
35   envp->__fpcr = fpcr;
36   envp->__fpsr = fpsr;
37 
38   /* Clear exception flags and set all exceptions to non-stop.  */
39   new_fpcr = fpcr & ~(FE_ALL_EXCEPT << FE_EXCEPT_SHIFT);
40   new_fpsr = fpsr & ~FE_ALL_EXCEPT;
41 
42   if (__glibc_unlikely (new_fpcr != fpcr))
43     _FPU_SETCW (new_fpcr);
44 
45   if (new_fpsr != fpsr)
46     _FPU_SETFPSR (new_fpsr);
47 }
48 
49 #define libc_feholdexcept  libc_feholdexcept_aarch64
50 #define libc_feholdexceptf libc_feholdexcept_aarch64
51 #define libc_feholdexceptl libc_feholdexcept_aarch64
52 
53 static __always_inline void
libc_fesetround_aarch64(int round)54 libc_fesetround_aarch64 (int round)
55 {
56   fpu_control_t fpcr;
57 
58   _FPU_GETCW (fpcr);
59 
60   /* Check whether rounding modes are different.  */
61   round = (fpcr ^ round) & _FPU_FPCR_RM_MASK;
62 
63   /* Set new rounding mode if different.  */
64   if (__glibc_unlikely (round != 0))
65     _FPU_SETCW (fpcr ^ round);
66 }
67 
68 #define libc_fesetround  libc_fesetround_aarch64
69 #define libc_fesetroundf libc_fesetround_aarch64
70 #define libc_fesetroundl libc_fesetround_aarch64
71 
72 static __always_inline void
libc_feholdexcept_setround_aarch64(fenv_t * envp,int round)73 libc_feholdexcept_setround_aarch64 (fenv_t *envp, int round)
74 {
75   fpu_control_t fpcr;
76   fpu_control_t new_fpcr;
77   fpu_fpsr_t fpsr;
78   fpu_fpsr_t new_fpsr;
79 
80   _FPU_GETCW (fpcr);
81   _FPU_GETFPSR (fpsr);
82   envp->__fpcr = fpcr;
83   envp->__fpsr = fpsr;
84 
85   /* Clear exception flags, set all exceptions to non-stop,
86      and set new rounding mode.  */
87   new_fpcr = fpcr & ~((FE_ALL_EXCEPT << FE_EXCEPT_SHIFT) | _FPU_FPCR_RM_MASK);
88   new_fpcr |= round;
89   new_fpsr = fpsr & ~FE_ALL_EXCEPT;
90 
91   if (__glibc_unlikely (new_fpcr != fpcr))
92     _FPU_SETCW (new_fpcr);
93 
94   if (new_fpsr != fpsr)
95     _FPU_SETFPSR (new_fpsr);
96 }
97 
98 #define libc_feholdexcept_setround  libc_feholdexcept_setround_aarch64
99 #define libc_feholdexcept_setroundf libc_feholdexcept_setround_aarch64
100 #define libc_feholdexcept_setroundl libc_feholdexcept_setround_aarch64
101 
102 static __always_inline int
libc_fetestexcept_aarch64(int ex)103 libc_fetestexcept_aarch64 (int ex)
104 {
105   fpu_fpsr_t fpsr;
106 
107   _FPU_GETFPSR (fpsr);
108   return fpsr & ex & FE_ALL_EXCEPT;
109 }
110 
111 #define libc_fetestexcept  libc_fetestexcept_aarch64
112 #define libc_fetestexceptf libc_fetestexcept_aarch64
113 #define libc_fetestexceptl libc_fetestexcept_aarch64
114 
115 static __always_inline void
libc_fesetenv_aarch64(const fenv_t * envp)116 libc_fesetenv_aarch64 (const fenv_t *envp)
117 {
118   fpu_control_t fpcr;
119   fpu_control_t new_fpcr;
120 
121   _FPU_GETCW (fpcr);
122   new_fpcr = envp->__fpcr;
123 
124   if (__glibc_unlikely (fpcr != new_fpcr))
125     _FPU_SETCW (new_fpcr);
126 
127   _FPU_SETFPSR (envp->__fpsr);
128 }
129 
130 #define libc_fesetenv  libc_fesetenv_aarch64
131 #define libc_fesetenvf libc_fesetenv_aarch64
132 #define libc_fesetenvl libc_fesetenv_aarch64
133 #define libc_feresetround_noex  libc_fesetenv_aarch64
134 #define libc_feresetround_noexf libc_fesetenv_aarch64
135 #define libc_feresetround_noexl libc_fesetenv_aarch64
136 
137 static __always_inline int
libc_feupdateenv_test_aarch64(const fenv_t * envp,int ex)138 libc_feupdateenv_test_aarch64 (const fenv_t *envp, int ex)
139 {
140   fpu_control_t fpcr;
141   fpu_control_t new_fpcr;
142   fpu_fpsr_t fpsr;
143   fpu_fpsr_t new_fpsr;
144   int excepts;
145 
146   _FPU_GETCW (fpcr);
147   _FPU_GETFPSR (fpsr);
148 
149   /* Merge current exception flags with the saved fenv.  */
150   excepts = fpsr & FE_ALL_EXCEPT;
151   new_fpcr = envp->__fpcr;
152   new_fpsr = envp->__fpsr | excepts;
153 
154   if (__glibc_unlikely (fpcr != new_fpcr))
155     _FPU_SETCW (new_fpcr);
156 
157   if (fpsr != new_fpsr)
158     _FPU_SETFPSR (new_fpsr);
159 
160   /* Raise the exceptions if enabled in the new FP state.  */
161   if (__glibc_unlikely (excepts & (new_fpcr >> FE_EXCEPT_SHIFT)))
162     __feraiseexcept (excepts);
163 
164   return excepts & ex;
165 }
166 
167 #define libc_feupdateenv_test  libc_feupdateenv_test_aarch64
168 #define libc_feupdateenv_testf libc_feupdateenv_test_aarch64
169 #define libc_feupdateenv_testl libc_feupdateenv_test_aarch64
170 
171 static __always_inline void
libc_feupdateenv_aarch64(const fenv_t * envp)172 libc_feupdateenv_aarch64 (const fenv_t *envp)
173 {
174   libc_feupdateenv_test_aarch64 (envp, 0);
175 }
176 
177 #define libc_feupdateenv  libc_feupdateenv_aarch64
178 #define libc_feupdateenvf libc_feupdateenv_aarch64
179 #define libc_feupdateenvl libc_feupdateenv_aarch64
180 
181 static __always_inline void
libc_feholdsetround_aarch64(fenv_t * envp,int round)182 libc_feholdsetround_aarch64 (fenv_t *envp, int round)
183 {
184   fpu_control_t fpcr;
185   fpu_fpsr_t fpsr;
186 
187   _FPU_GETCW (fpcr);
188   _FPU_GETFPSR (fpsr);
189   envp->__fpcr = fpcr;
190   envp->__fpsr = fpsr;
191 
192   /* Check whether rounding modes are different.  */
193   round = (fpcr ^ round) & _FPU_FPCR_RM_MASK;
194 
195   /* Set new rounding mode if different.  */
196   if (__glibc_unlikely (round != 0))
197     _FPU_SETCW (fpcr ^ round);
198 }
199 
200 #define libc_feholdsetround  libc_feholdsetround_aarch64
201 #define libc_feholdsetroundf libc_feholdsetround_aarch64
202 #define libc_feholdsetroundl libc_feholdsetround_aarch64
203 
204 static __always_inline void
libc_feresetround_aarch64(fenv_t * envp)205 libc_feresetround_aarch64 (fenv_t *envp)
206 {
207   fpu_control_t fpcr;
208   int round;
209 
210   _FPU_GETCW (fpcr);
211 
212   /* Check whether rounding modes are different.  */
213   round = (envp->__fpcr ^ fpcr) & _FPU_FPCR_RM_MASK;
214 
215   /* Restore the rounding mode if it was changed.  */
216   if (__glibc_unlikely (round != 0))
217     _FPU_SETCW (fpcr ^ round);
218 }
219 
220 #define libc_feresetround  libc_feresetround_aarch64
221 #define libc_feresetroundf libc_feresetround_aarch64
222 #define libc_feresetroundl libc_feresetround_aarch64
223 
224 /* We have support for rounding mode context.  */
225 #define HAVE_RM_CTX 1
226 
227 static __always_inline void
libc_feholdsetround_aarch64_ctx(struct rm_ctx * ctx,int r)228 libc_feholdsetround_aarch64_ctx (struct rm_ctx *ctx, int r)
229 {
230   fpu_control_t fpcr;
231   int round;
232 
233   _FPU_GETCW (fpcr);
234   ctx->env.__fpcr = fpcr;
235 
236   /* Check whether rounding modes are different.  */
237   round = (fpcr ^ r) & _FPU_FPCR_RM_MASK;
238   ctx->updated_status = round != 0;
239 
240   /* Set the rounding mode if changed.  */
241   if (__glibc_unlikely (round != 0))
242     _FPU_SETCW (fpcr ^ round);
243 }
244 
245 #define libc_feholdsetround_ctx		libc_feholdsetround_aarch64_ctx
246 #define libc_feholdsetroundf_ctx	libc_feholdsetround_aarch64_ctx
247 #define libc_feholdsetroundl_ctx	libc_feholdsetround_aarch64_ctx
248 
249 static __always_inline void
libc_feresetround_aarch64_ctx(struct rm_ctx * ctx)250 libc_feresetround_aarch64_ctx (struct rm_ctx *ctx)
251 {
252   /* Restore the rounding mode if updated.  */
253   if (__glibc_unlikely (ctx->updated_status))
254     _FPU_SETCW (ctx->env.__fpcr);
255 }
256 
257 #define libc_feresetround_ctx		libc_feresetround_aarch64_ctx
258 #define libc_feresetroundf_ctx		libc_feresetround_aarch64_ctx
259 #define libc_feresetroundl_ctx		libc_feresetround_aarch64_ctx
260 
261 static __always_inline void
libc_feholdsetround_noex_aarch64_ctx(struct rm_ctx * ctx,int r)262 libc_feholdsetround_noex_aarch64_ctx (struct rm_ctx *ctx, int r)
263 {
264   fpu_control_t fpcr;
265   fpu_fpsr_t fpsr;
266   int round;
267 
268   _FPU_GETCW (fpcr);
269   _FPU_GETFPSR (fpsr);
270   ctx->env.__fpcr = fpcr;
271   ctx->env.__fpsr = fpsr;
272 
273   /* Check whether rounding modes are different.  */
274   round = (fpcr ^ r) & _FPU_FPCR_RM_MASK;
275   ctx->updated_status = round != 0;
276 
277   /* Set the rounding mode if changed.  */
278   if (__glibc_unlikely (round != 0))
279     _FPU_SETCW (fpcr ^ round);
280 }
281 
282 #define libc_feholdsetround_noex_ctx	libc_feholdsetround_noex_aarch64_ctx
283 #define libc_feholdsetround_noexf_ctx	libc_feholdsetround_noex_aarch64_ctx
284 #define libc_feholdsetround_noexl_ctx	libc_feholdsetround_noex_aarch64_ctx
285 
286 static __always_inline void
libc_feresetround_noex_aarch64_ctx(struct rm_ctx * ctx)287 libc_feresetround_noex_aarch64_ctx (struct rm_ctx *ctx)
288 {
289   /* Restore the rounding mode if updated.  */
290   if (__glibc_unlikely (ctx->updated_status))
291     _FPU_SETCW (ctx->env.__fpcr);
292 
293   /* Write new FPSR to restore exception flags.  */
294   _FPU_SETFPSR (ctx->env.__fpsr);
295 }
296 
297 #define libc_feresetround_noex_ctx	libc_feresetround_noex_aarch64_ctx
298 #define libc_feresetround_noexf_ctx	libc_feresetround_noex_aarch64_ctx
299 #define libc_feresetround_noexl_ctx	libc_feresetround_noex_aarch64_ctx
300 
301 #include_next <fenv_private.h>
302 
303 #endif
304