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