1  /* FPU-related code for aarch64.
       2     Copyright (C) 2020-2023 Free Software Foundation, Inc.
       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
     296  support_fpu_rounding_mode (int mode)
     297  {
     298    if (mode == GFC_FPE_AWAY)
     299      return 0;
     300    else
     301      return 1;
     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  }