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