1 /* Private floating point rounding and exceptions handling.  ARM VFP 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 ARM_FENV_PRIVATE_H
20 #define ARM_FENV_PRIVATE_H 1
21 
22 #include <fenv.h>
23 #include <fpu_control.h>
24 
25 static __always_inline void
libc_feholdexcept_vfp(fenv_t * envp)26 libc_feholdexcept_vfp (fenv_t *envp)
27 {
28   fpu_control_t fpscr;
29 
30   _FPU_GETCW (fpscr);
31   envp->__cw = fpscr;
32 
33   /* Clear exception flags and set all exceptions to non-stop.  */
34   fpscr &= ~_FPU_MASK_EXCEPT;
35   _FPU_SETCW (fpscr);
36 }
37 
38 static __always_inline void
libc_fesetround_vfp(int round)39 libc_fesetround_vfp (int round)
40 {
41   fpu_control_t fpscr;
42 
43   _FPU_GETCW (fpscr);
44 
45   /* Set new rounding mode if different.  */
46   if (__glibc_unlikely ((fpscr & _FPU_MASK_RM) != round))
47     _FPU_SETCW ((fpscr & ~_FPU_MASK_RM) | round);
48 }
49 
50 static __always_inline void
libc_feholdexcept_setround_vfp(fenv_t * envp,int round)51 libc_feholdexcept_setround_vfp (fenv_t *envp, int round)
52 {
53   fpu_control_t fpscr;
54 
55   _FPU_GETCW (fpscr);
56   envp->__cw = fpscr;
57 
58   /* Clear exception flags, set all exceptions to non-stop,
59      and set new rounding mode.  */
60   fpscr &= ~(_FPU_MASK_EXCEPT | _FPU_MASK_RM);
61   _FPU_SETCW (fpscr | round);
62 }
63 
64 static __always_inline void
libc_feholdsetround_vfp(fenv_t * envp,int round)65 libc_feholdsetround_vfp (fenv_t *envp, int round)
66 {
67   fpu_control_t fpscr;
68 
69   _FPU_GETCW (fpscr);
70   envp->__cw = fpscr;
71 
72   /* Set new rounding mode if different.  */
73   if (__glibc_unlikely ((fpscr & _FPU_MASK_RM) != round))
74     _FPU_SETCW ((fpscr & ~_FPU_MASK_RM) | round);
75 }
76 
77 static __always_inline void
libc_feresetround_vfp(fenv_t * envp)78 libc_feresetround_vfp (fenv_t *envp)
79 {
80   fpu_control_t fpscr, round;
81 
82   _FPU_GETCW (fpscr);
83 
84   /* Check whether rounding modes are different.  */
85   round = (envp->__cw ^ fpscr) & _FPU_MASK_RM;
86 
87   /* Restore the rounding mode if it was changed.  */
88   if (__glibc_unlikely (round != 0))
89     _FPU_SETCW (fpscr ^ round);
90 }
91 
92 static __always_inline int
libc_fetestexcept_vfp(int ex)93 libc_fetestexcept_vfp (int ex)
94 {
95   fpu_control_t fpscr;
96 
97   _FPU_GETCW (fpscr);
98   return fpscr & ex & FE_ALL_EXCEPT;
99 }
100 
101 static __always_inline void
libc_fesetenv_vfp(const fenv_t * envp)102 libc_fesetenv_vfp (const fenv_t *envp)
103 {
104   fpu_control_t fpscr, new_fpscr;
105 
106   _FPU_GETCW (fpscr);
107   new_fpscr = envp->__cw;
108 
109   /* Write new FPSCR if different (ignoring NZCV flags).  */
110   if (__glibc_unlikely (((fpscr ^ new_fpscr) & ~_FPU_MASK_NZCV) != 0))
111     _FPU_SETCW (new_fpscr);
112 }
113 
114 static __always_inline int
libc_feupdateenv_test_vfp(const fenv_t * envp,int ex)115 libc_feupdateenv_test_vfp (const fenv_t *envp, int ex)
116 {
117   fpu_control_t fpscr, new_fpscr;
118   int excepts;
119 
120   _FPU_GETCW (fpscr);
121 
122   /* Merge current exception flags with the saved fenv.  */
123   excepts = fpscr & FE_ALL_EXCEPT;
124   new_fpscr = envp->__cw | excepts;
125 
126   /* Write new FPSCR if different (ignoring NZCV flags).  */
127   if (__glibc_unlikely (((fpscr ^ new_fpscr) & ~_FPU_MASK_NZCV) != 0))
128     _FPU_SETCW (new_fpscr);
129 
130   /* Raise the exceptions if enabled in the new FP state.  */
131   if (__glibc_unlikely (excepts & (new_fpscr >> FE_EXCEPT_SHIFT)))
132     __feraiseexcept (excepts);
133 
134   return excepts & ex;
135 }
136 
137 static __always_inline void
libc_feupdateenv_vfp(const fenv_t * envp)138 libc_feupdateenv_vfp (const fenv_t *envp)
139 {
140   libc_feupdateenv_test_vfp (envp, 0);
141 }
142 
143 static __always_inline void
libc_feholdsetround_vfp_ctx(struct rm_ctx * ctx,int r)144 libc_feholdsetround_vfp_ctx (struct rm_ctx *ctx, int r)
145 {
146   fpu_control_t fpscr, round;
147 
148   _FPU_GETCW (fpscr);
149   ctx->updated_status = false;
150   ctx->env.__cw = fpscr;
151 
152   /* Check whether rounding modes are different.  */
153   round = (fpscr ^ r) & _FPU_MASK_RM;
154 
155   /* Set the rounding mode if changed.  */
156   if (__glibc_unlikely (round != 0))
157     {
158       ctx->updated_status = true;
159       _FPU_SETCW (fpscr ^ round);
160     }
161 }
162 
163 static __always_inline void
libc_feresetround_vfp_ctx(struct rm_ctx * ctx)164 libc_feresetround_vfp_ctx (struct rm_ctx *ctx)
165 {
166   /* Restore the rounding mode if updated.  */
167   if (__glibc_unlikely (ctx->updated_status))
168     {
169       fpu_control_t fpscr;
170 
171       _FPU_GETCW (fpscr);
172       fpscr = (fpscr & ~_FPU_MASK_RM) | (ctx->env.__cw & _FPU_MASK_RM);
173       _FPU_SETCW (fpscr);
174     }
175 }
176 
177 static __always_inline void
libc_fesetenv_vfp_ctx(struct rm_ctx * ctx)178 libc_fesetenv_vfp_ctx (struct rm_ctx *ctx)
179 {
180   fpu_control_t fpscr, new_fpscr;
181 
182   _FPU_GETCW (fpscr);
183   new_fpscr = ctx->env.__cw;
184 
185   /* Write new FPSCR if different (ignoring NZCV flags).  */
186   if (__glibc_unlikely (((fpscr ^ new_fpscr) & ~_FPU_MASK_NZCV) != 0))
187     _FPU_SETCW (new_fpscr);
188 }
189 
190 #ifndef __SOFTFP__
191 
192 # define libc_feholdexcept  libc_feholdexcept_vfp
193 # define libc_feholdexceptf libc_feholdexcept_vfp
194 # define libc_feholdexceptl libc_feholdexcept_vfp
195 
196 # define libc_fesetround  libc_fesetround_vfp
197 # define libc_fesetroundf libc_fesetround_vfp
198 # define libc_fesetroundl libc_fesetround_vfp
199 
200 # define libc_feresetround  libc_feresetround_vfp
201 # define libc_feresetroundf libc_feresetround_vfp
202 # define libc_feresetroundl libc_feresetround_vfp
203 
204 # define libc_feresetround_noex  libc_fesetenv_vfp
205 # define libc_feresetround_noexf libc_fesetenv_vfp
206 # define libc_feresetround_noexl libc_fesetenv_vfp
207 
208 # define libc_feholdexcept_setround  libc_feholdexcept_setround_vfp
209 # define libc_feholdexcept_setroundf libc_feholdexcept_setround_vfp
210 # define libc_feholdexcept_setroundl libc_feholdexcept_setround_vfp
211 
212 # define libc_feholdsetround  libc_feholdsetround_vfp
213 # define libc_feholdsetroundf libc_feholdsetround_vfp
214 # define libc_feholdsetroundl libc_feholdsetround_vfp
215 
216 # define libc_fetestexcept  libc_fetestexcept_vfp
217 # define libc_fetestexceptf libc_fetestexcept_vfp
218 # define libc_fetestexceptl libc_fetestexcept_vfp
219 
220 # define libc_fesetenv  libc_fesetenv_vfp
221 # define libc_fesetenvf libc_fesetenv_vfp
222 # define libc_fesetenvl libc_fesetenv_vfp
223 
224 # define libc_feupdateenv  libc_feupdateenv_vfp
225 # define libc_feupdateenvf libc_feupdateenv_vfp
226 # define libc_feupdateenvl libc_feupdateenv_vfp
227 
228 # define libc_feupdateenv_test  libc_feupdateenv_test_vfp
229 # define libc_feupdateenv_testf libc_feupdateenv_test_vfp
230 # define libc_feupdateenv_testl libc_feupdateenv_test_vfp
231 
232 /* We have support for rounding mode context.  */
233 #define HAVE_RM_CTX 1
234 
235 # define libc_feholdsetround_ctx	libc_feholdsetround_vfp_ctx
236 # define libc_feresetround_ctx		libc_feresetround_vfp_ctx
237 # define libc_feresetround_noex_ctx	libc_fesetenv_vfp_ctx
238 
239 # define libc_feholdsetroundf_ctx	libc_feholdsetround_vfp_ctx
240 # define libc_feresetroundf_ctx		libc_feresetround_vfp_ctx
241 # define libc_feresetround_noexf_ctx	libc_fesetenv_vfp_ctx
242 
243 # define libc_feholdsetroundl_ctx	libc_feholdsetround_vfp_ctx
244 # define libc_feresetroundl_ctx		libc_feresetround_vfp_ctx
245 # define libc_feresetround_noexl_ctx	libc_fesetenv_vfp_ctx
246 
247 #endif
248 
249 #include_next <fenv_private.h>
250 
251 #endif /* ARM_FENV_PRIVATE_H */
252