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