1 /* Private floating point rounding and exceptions handling.  ARM VFP version.
2    Copyright (C) 2014-2025 Free Software Foundation, Inc.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library.  If not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #ifndef ARM_FENV_PRIVATE_H
19 #define ARM_FENV_PRIVATE_H 1
20 
21 #include <fenv.h>
22 #include <fpu_control.h>
23 
24 static __always_inline void
libc_feholdexcept_vfp(fenv_t * envp)25 libc_feholdexcept_vfp (fenv_t *envp)
26 {
27   fpu_control_t fpscr;
28 
29   _FPU_GETCW (fpscr);
30   envp->__cw = fpscr;
31 
32   /* Clear exception flags and set all exceptions to non-stop.  */
33   fpscr &= ~_FPU_MASK_EXCEPT;
34   _FPU_SETCW (fpscr);
35 }
36 
37 static __always_inline void
libc_fesetround_vfp(int round)38 libc_fesetround_vfp (int round)
39 {
40   fpu_control_t fpscr;
41 
42   _FPU_GETCW (fpscr);
43 
44   /* Set new rounding mode if different.  */
45   if (unlikely ((fpscr & _FPU_MASK_RM) != round))
46     _FPU_SETCW ((fpscr & ~_FPU_MASK_RM) | round);
47 }
48 
49 static __always_inline void
libc_feholdexcept_setround_vfp(fenv_t * envp,int round)50 libc_feholdexcept_setround_vfp (fenv_t *envp, int round)
51 {
52   fpu_control_t fpscr;
53 
54   _FPU_GETCW (fpscr);
55   envp->__cw = fpscr;
56 
57   /* Clear exception flags, set all exceptions to non-stop,
58      and set new rounding mode.  */
59   fpscr &= ~(_FPU_MASK_EXCEPT | _FPU_MASK_RM);
60   _FPU_SETCW (fpscr | round);
61 }
62 
63 static __always_inline void
libc_feholdsetround_vfp(fenv_t * envp,int round)64 libc_feholdsetround_vfp (fenv_t *envp, int round)
65 {
66   fpu_control_t fpscr;
67 
68   _FPU_GETCW (fpscr);
69   envp->__cw = fpscr;
70 
71   /* Set new rounding mode if different.  */
72   if (unlikely ((fpscr & _FPU_MASK_RM) != round))
73     _FPU_SETCW ((fpscr & ~_FPU_MASK_RM) | round);
74 }
75 
76 static __always_inline void
libc_feresetround_vfp(fenv_t * envp)77 libc_feresetround_vfp (fenv_t *envp)
78 {
79   fpu_control_t fpscr, round;
80 
81   _FPU_GETCW (fpscr);
82 
83   /* Check whether rounding modes are different.  */
84   round = (envp->__cw ^ fpscr) & _FPU_MASK_RM;
85 
86   /* Restore the rounding mode if it was changed.  */
87   if (unlikely (round != 0))
88     _FPU_SETCW (fpscr ^ round);
89 }
90 
91 static __always_inline int
libc_fetestexcept_vfp(int ex)92 libc_fetestexcept_vfp (int ex)
93 {
94   fpu_control_t fpscr;
95 
96   _FPU_GETCW (fpscr);
97   return fpscr & ex & FE_ALL_EXCEPT;
98 }
99 
100 static __always_inline void
libc_fesetenv_vfp(const fenv_t * envp)101 libc_fesetenv_vfp (const fenv_t *envp)
102 {
103   fpu_control_t fpscr, new_fpscr;
104 
105   _FPU_GETCW (fpscr);
106   new_fpscr = envp->__cw;
107 
108   /* Write new FPSCR if different (ignoring NZCV flags).  */
109   if (unlikely (((fpscr ^ new_fpscr) & ~_FPU_MASK_NZCV) != 0))
110     _FPU_SETCW (new_fpscr);
111 }
112 
113 static __always_inline int
libc_feupdateenv_test_vfp(const fenv_t * envp,int ex)114 libc_feupdateenv_test_vfp (const fenv_t *envp, int ex)
115 {
116   fpu_control_t fpscr, new_fpscr;
117   int excepts;
118 
119   _FPU_GETCW (fpscr);
120 
121   /* Merge current exception flags with the saved fenv.  */
122   excepts = fpscr & FE_ALL_EXCEPT;
123   new_fpscr = envp->__cw | excepts;
124 
125   /* Write new FPSCR if different (ignoring NZCV flags).  */
126   if (unlikely (((fpscr ^ new_fpscr) & ~_FPU_MASK_NZCV) != 0))
127     _FPU_SETCW (new_fpscr);
128 
129   /* Raise the exceptions if enabled in the new FP state.  */
130   if (unlikely (excepts & (new_fpscr >> FE_EXCEPT_SHIFT)))
131     feraiseexcept (excepts);
132 
133   return excepts & ex;
134 }
135 
136 static __always_inline void
libc_feupdateenv_vfp(const fenv_t * envp)137 libc_feupdateenv_vfp (const fenv_t *envp)
138 {
139   libc_feupdateenv_test_vfp (envp, 0);
140 }
141 
142 static __always_inline void
libc_feholdsetround_vfp_ctx(struct rm_ctx * ctx,int r)143 libc_feholdsetround_vfp_ctx (struct rm_ctx *ctx, int r)
144 {
145   fpu_control_t fpscr, round;
146 
147   _FPU_GETCW (fpscr);
148   ctx->updated_status = false;
149   ctx->env.__cw = fpscr;
150 
151   /* Check whether rounding modes are different.  */
152   round = (fpscr ^ r) & _FPU_MASK_RM;
153 
154   /* Set the rounding mode if changed.  */
155   if (unlikely (round != 0))
156     {
157       ctx->updated_status = true;
158       _FPU_SETCW (fpscr ^ round);
159     }
160 }
161 
162 static __always_inline void
libc_feresetround_vfp_ctx(struct rm_ctx * ctx)163 libc_feresetround_vfp_ctx (struct rm_ctx *ctx)
164 {
165   /* Restore the rounding mode if updated.  */
166   if (unlikely (ctx->updated_status))
167     {
168       fpu_control_t fpscr;
169 
170       _FPU_GETCW (fpscr);
171       fpscr = (fpscr & ~_FPU_MASK_RM) | (ctx->env.__cw & _FPU_MASK_RM);
172       _FPU_SETCW (fpscr);
173     }
174 }
175 
176 static __always_inline void
libc_fesetenv_vfp_ctx(struct rm_ctx * ctx)177 libc_fesetenv_vfp_ctx (struct rm_ctx *ctx)
178 {
179   fpu_control_t fpscr, new_fpscr;
180 
181   _FPU_GETCW (fpscr);
182   new_fpscr = ctx->env.__cw;
183 
184   /* Write new FPSCR if different (ignoring NZCV flags).  */
185   if (unlikely (((fpscr ^ new_fpscr) & ~_FPU_MASK_NZCV) != 0))
186     _FPU_SETCW (new_fpscr);
187 }
188 
189 #ifndef __SOFTFP__
190 
191 # define libc_feholdexcept  libc_feholdexcept_vfp
192 # define libc_feholdexceptf libc_feholdexcept_vfp
193 # define libc_feholdexceptl libc_feholdexcept_vfp
194 
195 # define libc_fesetround  libc_fesetround_vfp
196 # define libc_fesetroundf libc_fesetround_vfp
197 # define libc_fesetroundl libc_fesetround_vfp
198 
199 # define libc_feresetround  libc_feresetround_vfp
200 # define libc_feresetroundf libc_feresetround_vfp
201 # define libc_feresetroundl libc_feresetround_vfp
202 
203 # define libc_feresetround_noex  libc_fesetenv_vfp
204 # define libc_feresetround_noexf libc_fesetenv_vfp
205 # define libc_feresetround_noexl libc_fesetenv_vfp
206 
207 # define libc_feholdexcept_setround  libc_feholdexcept_setround_vfp
208 # define libc_feholdexcept_setroundf libc_feholdexcept_setround_vfp
209 # define libc_feholdexcept_setroundl libc_feholdexcept_setround_vfp
210 
211 # define libc_feholdsetround  libc_feholdsetround_vfp
212 # define libc_feholdsetroundf libc_feholdsetround_vfp
213 # define libc_feholdsetroundl libc_feholdsetround_vfp
214 
215 # define libc_fetestexcept  libc_fetestexcept_vfp
216 # define libc_fetestexceptf libc_fetestexcept_vfp
217 # define libc_fetestexceptl libc_fetestexcept_vfp
218 
219 # define libc_fesetenv  libc_fesetenv_vfp
220 # define libc_fesetenvf libc_fesetenv_vfp
221 # define libc_fesetenvl libc_fesetenv_vfp
222 
223 # define libc_feupdateenv  libc_feupdateenv_vfp
224 # define libc_feupdateenvf libc_feupdateenv_vfp
225 # define libc_feupdateenvl libc_feupdateenv_vfp
226 
227 # define libc_feupdateenv_test  libc_feupdateenv_test_vfp
228 # define libc_feupdateenv_testf libc_feupdateenv_test_vfp
229 # define libc_feupdateenv_testl libc_feupdateenv_test_vfp
230 
231 /* We have support for rounding mode context.  */
232 #define HAVE_RM_CTX 1
233 
234 # define libc_feholdsetround_ctx	libc_feholdsetround_vfp_ctx
235 # define libc_feresetround_ctx		libc_feresetround_vfp_ctx
236 # define libc_feresetround_noex_ctx	libc_fesetenv_vfp_ctx
237 
238 # define libc_feholdsetroundf_ctx	libc_feholdsetround_vfp_ctx
239 # define libc_feresetroundf_ctx		libc_feresetround_vfp_ctx
240 # define libc_feresetround_noexf_ctx	libc_fesetenv_vfp_ctx
241 
242 # define libc_feholdsetroundl_ctx	libc_feholdsetround_vfp_ctx
243 # define libc_feresetroundl_ctx		libc_feresetround_vfp_ctx
244 # define libc_feresetround_noexl_ctx	libc_fesetenv_vfp_ctx
245 
246 #endif
247 
248 #endif /* ARM_FENV_PRIVATE_H */
249