]>
Commit | Line | Data |
---|---|---|
220b9bdf | 1 | /* FPU-related code for aarch64. |
a945c346 | 2 | Copyright (C) 2020-2024 Free Software Foundation, Inc. |
220b9bdf FXC |
3 | Contributed by Francois-Xavier Coudert <fxcoudert@gcc.gnu.org> |
4 | ||
5 | This file is part of the GNU Fortran runtime library (libgfortran). | |
6 | ||
7 | Libgfortran is free software; you can redistribute it and/or | |
8 | modify it under the terms of the GNU General Public | |
9 | License as published by the Free Software Foundation; either | |
10 | version 3 of the License, or (at your option) any later version. | |
11 | ||
12 | Libgfortran is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | Under Section 7 of GPL version 3, you are granted additional | |
18 | permissions described in the GCC Runtime Library Exception, version | |
19 | 3.1, as published by the Free Software Foundation. | |
20 | ||
21 | You should have received a copy of the GNU General Public License and | |
22 | a copy of the GCC Runtime Library Exception along with this program; | |
23 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
24 | <http://www.gnu.org/licenses/>. */ | |
25 | ||
26 | ||
27 | /* Rounding mask and modes */ | |
28 | ||
29 | #define FPCR_RM_MASK 0x0c00000 | |
30 | #define FE_TONEAREST 0x0000000 | |
31 | #define FE_UPWARD 0x0400000 | |
32 | #define FE_DOWNWARD 0x0800000 | |
33 | #define FE_TOWARDZERO 0x0c00000 | |
34 | #define FE_MAP_FZ 0x1000000 | |
35 | ||
36 | /* Exceptions */ | |
37 | ||
38 | #define FE_INVALID 1 | |
39 | #define FE_DIVBYZERO 2 | |
40 | #define FE_OVERFLOW 4 | |
41 | #define FE_UNDERFLOW 8 | |
42 | #define FE_INEXACT 16 | |
43 | ||
44 | #define FE_ALL_EXCEPT (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT) | |
45 | #define FE_EXCEPT_SHIFT 8 | |
46 | ||
47 | ||
48 | ||
49 | /* This structure corresponds to the layout of the block | |
50 | written by FSTENV. */ | |
51 | struct fenv | |
52 | { | |
53 | unsigned int __fpcr; | |
54 | unsigned int __fpsr; | |
55 | }; | |
56 | ||
57 | /* Check we can actually store the FPU state in the allocated size. */ | |
58 | _Static_assert (sizeof(struct fenv) <= (size_t) GFC_FPE_STATE_BUFFER_SIZE, | |
59 | "GFC_FPE_STATE_BUFFER_SIZE is too small"); | |
60 | ||
61 | ||
62 | ||
63 | void | |
64 | set_fpu (void) | |
65 | { | |
66 | if (options.fpe & GFC_FPE_DENORMAL) | |
67 | estr_write ("Fortran runtime warning: Floating point 'denormal operand' " | |
68 | "exception not supported.\n"); | |
69 | ||
70 | set_fpu_trap_exceptions (options.fpe, 0); | |
71 | } | |
72 | ||
73 | ||
74 | int | |
75 | get_fpu_trap_exceptions (void) | |
76 | { | |
77 | unsigned int fpcr, exceptions; | |
78 | int res = 0; | |
79 | ||
80 | fpcr = __builtin_aarch64_get_fpcr(); | |
81 | exceptions = (fpcr >> FE_EXCEPT_SHIFT) & FE_ALL_EXCEPT; | |
82 | ||
83 | if (exceptions & FE_INVALID) res |= GFC_FPE_INVALID; | |
84 | if (exceptions & FE_DIVBYZERO) res |= GFC_FPE_ZERO; | |
85 | if (exceptions & FE_OVERFLOW) res |= GFC_FPE_OVERFLOW; | |
86 | if (exceptions & FE_UNDERFLOW) res |= GFC_FPE_UNDERFLOW; | |
87 | if (exceptions & FE_INEXACT) res |= GFC_FPE_INEXACT; | |
88 | ||
89 | return res; | |
90 | } | |
91 | ||
92 | ||
93 | void set_fpu_trap_exceptions (int trap, int notrap) | |
94 | { | |
95 | unsigned int mode_set = 0, mode_clr = 0; | |
96 | unsigned int fpsr, fpsr_new; | |
97 | unsigned int fpcr, fpcr_new; | |
98 | ||
99 | if (trap & GFC_FPE_INVALID) | |
100 | mode_set |= FE_INVALID; | |
101 | if (notrap & GFC_FPE_INVALID) | |
102 | mode_clr |= FE_INVALID; | |
103 | ||
104 | if (trap & GFC_FPE_ZERO) | |
105 | mode_set |= FE_DIVBYZERO; | |
106 | if (notrap & GFC_FPE_ZERO) | |
107 | mode_clr |= FE_DIVBYZERO; | |
108 | ||
109 | if (trap & GFC_FPE_OVERFLOW) | |
110 | mode_set |= FE_OVERFLOW; | |
111 | if (notrap & GFC_FPE_OVERFLOW) | |
112 | mode_clr |= FE_OVERFLOW; | |
113 | ||
114 | if (trap & GFC_FPE_UNDERFLOW) | |
115 | mode_set |= FE_UNDERFLOW; | |
116 | if (notrap & GFC_FPE_UNDERFLOW) | |
117 | mode_clr |= FE_UNDERFLOW; | |
118 | ||
119 | if (trap & GFC_FPE_INEXACT) | |
120 | mode_set |= FE_INEXACT; | |
121 | if (notrap & GFC_FPE_INEXACT) | |
122 | mode_clr |= FE_INEXACT; | |
123 | ||
124 | /* Clear stalled exception flags. */ | |
125 | fpsr = __builtin_aarch64_get_fpsr(); | |
126 | fpsr_new = fpsr & ~FE_ALL_EXCEPT; | |
127 | if (fpsr_new != fpsr) | |
128 | __builtin_aarch64_set_fpsr(fpsr_new); | |
129 | ||
130 | fpcr_new = fpcr = __builtin_aarch64_get_fpcr(); | |
131 | fpcr_new |= (mode_set << FE_EXCEPT_SHIFT); | |
132 | fpcr_new &= ~(mode_clr << FE_EXCEPT_SHIFT); | |
133 | ||
134 | if (fpcr_new != fpcr) | |
135 | __builtin_aarch64_set_fpcr(fpcr_new); | |
136 | } | |
137 | ||
138 | ||
139 | int | |
140 | support_fpu_flag (int flag) | |
141 | { | |
142 | if (flag & GFC_FPE_DENORMAL) | |
143 | return 0; | |
144 | ||
145 | return 1; | |
146 | } | |
147 | ||
148 | ||
149 | int | |
150 | support_fpu_trap (int flag) | |
151 | { | |
152 | if (flag & GFC_FPE_DENORMAL) | |
153 | return 0; | |
154 | ||
155 | return 1; | |
156 | } | |
157 | ||
158 | ||
159 | int | |
160 | get_fpu_except_flags (void) | |
161 | { | |
162 | int result; | |
163 | unsigned int fpsr; | |
164 | ||
165 | result = 0; | |
166 | fpsr = __builtin_aarch64_get_fpsr() & FE_ALL_EXCEPT; | |
167 | ||
168 | if (fpsr & FE_INVALID) | |
169 | result |= GFC_FPE_INVALID; | |
170 | if (fpsr & FE_DIVBYZERO) | |
171 | result |= GFC_FPE_ZERO; | |
172 | if (fpsr & FE_OVERFLOW) | |
173 | result |= GFC_FPE_OVERFLOW; | |
174 | if (fpsr & FE_UNDERFLOW) | |
175 | result |= GFC_FPE_UNDERFLOW; | |
176 | if (fpsr & FE_INEXACT) | |
177 | result |= GFC_FPE_INEXACT; | |
178 | ||
179 | return result; | |
180 | } | |
181 | ||
182 | ||
183 | void | |
184 | set_fpu_except_flags (int set, int clear) | |
185 | { | |
186 | unsigned int exc_set = 0, exc_clr = 0; | |
187 | unsigned int fpsr, fpsr_new; | |
188 | ||
189 | if (set & GFC_FPE_INVALID) | |
190 | exc_set |= FE_INVALID; | |
191 | else if (clear & GFC_FPE_INVALID) | |
192 | exc_clr |= FE_INVALID; | |
193 | ||
194 | if (set & GFC_FPE_ZERO) | |
195 | exc_set |= FE_DIVBYZERO; | |
196 | else if (clear & GFC_FPE_ZERO) | |
197 | exc_clr |= FE_DIVBYZERO; | |
198 | ||
199 | if (set & GFC_FPE_OVERFLOW) | |
200 | exc_set |= FE_OVERFLOW; | |
201 | else if (clear & GFC_FPE_OVERFLOW) | |
202 | exc_clr |= FE_OVERFLOW; | |
203 | ||
204 | if (set & GFC_FPE_UNDERFLOW) | |
205 | exc_set |= FE_UNDERFLOW; | |
206 | else if (clear & GFC_FPE_UNDERFLOW) | |
207 | exc_clr |= FE_UNDERFLOW; | |
208 | ||
209 | if (set & GFC_FPE_INEXACT) | |
210 | exc_set |= FE_INEXACT; | |
211 | else if (clear & GFC_FPE_INEXACT) | |
212 | exc_clr |= FE_INEXACT; | |
213 | ||
214 | fpsr_new = fpsr = __builtin_aarch64_get_fpsr(); | |
215 | fpsr_new &= ~exc_clr; | |
216 | fpsr_new |= exc_set; | |
217 | ||
218 | if (fpsr_new != fpsr) | |
219 | __builtin_aarch64_set_fpsr(fpsr_new); | |
220 | } | |
221 | ||
222 | ||
223 | void | |
224 | get_fpu_state (void *state) | |
225 | { | |
226 | struct fenv *envp = state; | |
227 | envp->__fpcr = __builtin_aarch64_get_fpcr(); | |
228 | envp->__fpsr = __builtin_aarch64_get_fpsr(); | |
229 | } | |
230 | ||
231 | ||
232 | void | |
233 | set_fpu_state (void *state) | |
234 | { | |
235 | struct fenv *envp = state; | |
236 | __builtin_aarch64_set_fpcr(envp->__fpcr); | |
237 | __builtin_aarch64_set_fpsr(envp->__fpsr); | |
238 | } | |
239 | ||
240 | ||
241 | int | |
242 | get_fpu_rounding_mode (void) | |
243 | { | |
244 | unsigned int fpcr = __builtin_aarch64_get_fpcr(); | |
245 | fpcr &= FPCR_RM_MASK; | |
246 | ||
247 | switch (fpcr) | |
248 | { | |
249 | case FE_TONEAREST: | |
250 | return GFC_FPE_TONEAREST; | |
251 | case FE_UPWARD: | |
252 | return GFC_FPE_UPWARD; | |
253 | case FE_DOWNWARD: | |
254 | return GFC_FPE_DOWNWARD; | |
255 | case FE_TOWARDZERO: | |
256 | return GFC_FPE_TOWARDZERO; | |
257 | default: | |
258 | return 0; /* Should be unreachable. */ | |
259 | } | |
260 | } | |
261 | ||
262 | ||
263 | void | |
264 | set_fpu_rounding_mode (int round) | |
265 | { | |
266 | unsigned int fpcr, round_mode; | |
267 | ||
268 | switch (round) | |
269 | { | |
270 | case GFC_FPE_TONEAREST: | |
271 | round_mode = FE_TONEAREST; | |
272 | break; | |
273 | case GFC_FPE_UPWARD: | |
274 | round_mode = FE_UPWARD; | |
275 | break; | |
276 | case GFC_FPE_DOWNWARD: | |
277 | round_mode = FE_DOWNWARD; | |
278 | break; | |
279 | case GFC_FPE_TOWARDZERO: | |
280 | round_mode = FE_TOWARDZERO; | |
281 | break; | |
282 | default: | |
283 | return; /* Should be unreachable. */ | |
284 | } | |
285 | ||
286 | fpcr = __builtin_aarch64_get_fpcr(); | |
287 | ||
288 | /* Only set FPCR if requested mode is different from current. */ | |
289 | round_mode = (fpcr ^ round_mode) & FPCR_RM_MASK; | |
290 | if (round_mode != 0) | |
291 | __builtin_aarch64_set_fpcr(fpcr ^ round_mode); | |
292 | } | |
293 | ||
294 | ||
295 | int | |
4637a1d2 | 296 | support_fpu_rounding_mode (int mode) |
220b9bdf | 297 | { |
4637a1d2 FXC |
298 | if (mode == GFC_FPE_AWAY) |
299 | return 0; | |
300 | else | |
301 | return 1; | |
220b9bdf FXC |
302 | } |
303 | ||
304 | ||
305 | int | |
306 | support_fpu_underflow_control (int kind __attribute__((unused))) | |
307 | { | |
308 | /* Not supported for binary128. */ | |
309 | return (kind == 4 || kind == 8) ? 1 : 0; | |
310 | } | |
311 | ||
312 | ||
313 | int | |
314 | get_fpu_underflow_mode (void) | |
315 | { | |
316 | unsigned int fpcr = __builtin_aarch64_get_fpcr(); | |
317 | ||
318 | /* Return 0 for abrupt underflow (flush to zero), 1 for gradual underflow. */ | |
319 | return (fpcr & FE_MAP_FZ) ? 0 : 1; | |
320 | } | |
321 | ||
322 | ||
323 | void | |
324 | set_fpu_underflow_mode (int gradual __attribute__((unused))) | |
325 | { | |
326 | unsigned int fpcr = __builtin_aarch64_get_fpcr(); | |
327 | ||
328 | if (gradual) | |
329 | fpcr &= ~FE_MAP_FZ; | |
330 | else | |
331 | fpcr |= FE_MAP_FZ; | |
332 | ||
333 | __builtin_aarch64_set_fpcr(fpcr); | |
334 | } |