PR 78534, 83704 Handle large formatted I/O
[gcc.git] / libgfortran / config / fpu-387.h
index 66e2dbeecea7335d5e36ffbafe5b117c3e527d60..c6cb873fc0e631738aaf188304a26aa8328c6929 100644 (file)
@@ -1,5 +1,5 @@
 /* FPU-related code for x86 and x86_64 processors.
-   Copyright (C) 2005-2014 Free Software Foundation, Inc.
+   Copyright (C) 2005-2018 Free Software Foundation, Inc.
    Contributed by Francois-Xavier Coudert <coudert@clipper.ens.fr>
 
 This file is part of the GNU Fortran 95 runtime library (libgfortran).
@@ -23,8 +23,6 @@ a copy of the GCC Runtime Library Exception along with this program;
 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
-#include <assert.h>
-
 #ifndef __SSE_MATH__
 #include "cpuid.h"
 #endif
@@ -64,6 +62,11 @@ has_sse (void)
 
 #define _FPU_RC_MASK    0x3
 
+/* Enable flush to zero mode.  */
+
+#define MXCSR_FTZ (1 << 15)
+
+
 /* This structure corresponds to the layout of the block
    written by FSTENV.  */
 typedef struct
@@ -84,6 +87,10 @@ typedef struct
 }
 my_fenv_t;
 
+/* Check we can actually store the FPU state in the allocated size.  */
+_Static_assert (sizeof(my_fenv_t) <= (size_t) GFC_FPE_STATE_BUFFER_SIZE,
+               "GFC_FPE_STATE_BUFFER_SIZE is too small");
+
 
 /* Raise the supported floating-point exceptions from EXCEPTS.  Other
    bits in EXCEPTS are ignored.  Code originally borrowed from
@@ -208,11 +215,12 @@ set_fpu (void)
 int
 get_fpu_trap_exceptions (void)
 {
-  int res = 0;
   unsigned short cw;
+  int mask;
+  int res = 0;
 
   __asm__ __volatile__ ("fstcw\t%0" : "=m" (cw));
-  cw &= _FPU_MASK_ALL;
+  mask = cw;
 
   if (has_sse())
     {
@@ -221,15 +229,17 @@ get_fpu_trap_exceptions (void)
       __asm__ __volatile__ ("%vstmxcsr\t%0" : "=m" (cw_sse));
 
       /* The SSE exception masks are shifted by 7 bits.  */
-      cw = cw | ((cw_sse >> 7) & _FPU_MASK_ALL);
+      mask |= (cw_sse >> 7);
     }
 
-  if (~cw & _FPU_MASK_IM) res |= GFC_FPE_INVALID;
-  if (~cw & _FPU_MASK_DM) res |= GFC_FPE_DENORMAL;
-  if (~cw & _FPU_MASK_ZM) res |= GFC_FPE_ZERO;
-  if (~cw & _FPU_MASK_OM) res |= GFC_FPE_OVERFLOW;
-  if (~cw & _FPU_MASK_UM) res |= GFC_FPE_UNDERFLOW;
-  if (~cw & _FPU_MASK_PM) res |= GFC_FPE_INEXACT;
+  mask = ~mask & _FPU_MASK_ALL;
+
+  if (mask & _FPU_MASK_IM) res |= GFC_FPE_INVALID;
+  if (mask & _FPU_MASK_DM) res |= GFC_FPE_DENORMAL;
+  if (mask & _FPU_MASK_ZM) res |= GFC_FPE_ZERO;
+  if (mask & _FPU_MASK_OM) res |= GFC_FPE_OVERFLOW;
+  if (mask & _FPU_MASK_UM) res |= GFC_FPE_UNDERFLOW;
+  if (mask & _FPU_MASK_PM) res |= GFC_FPE_INEXACT;
 
   return res;
 }
@@ -245,7 +255,7 @@ get_fpu_except_flags (void)
 {
   unsigned short cw;
   int excepts;
-  int result = 0;
+  int res = 0;
 
   __asm__ __volatile__ ("fnstsw\t%0" : "=am" (cw));
   excepts = cw;
@@ -260,14 +270,14 @@ get_fpu_except_flags (void)
 
   excepts &= _FPU_EX_ALL;
 
-  if (excepts & _FPU_MASK_IM) result |= GFC_FPE_INVALID;
-  if (excepts & _FPU_MASK_DM) result |= GFC_FPE_DENORMAL;
-  if (excepts & _FPU_MASK_ZM) result |= GFC_FPE_ZERO;
-  if (excepts & _FPU_MASK_OM) result |= GFC_FPE_OVERFLOW;
-  if (excepts & _FPU_MASK_UM) result |= GFC_FPE_UNDERFLOW;
-  if (excepts & _FPU_MASK_PM) result |= GFC_FPE_INEXACT;
+  if (excepts & _FPU_MASK_IM) res |= GFC_FPE_INVALID;
+  if (excepts & _FPU_MASK_DM) res |= GFC_FPE_DENORMAL;
+  if (excepts & _FPU_MASK_ZM) res |= GFC_FPE_ZERO;
+  if (excepts & _FPU_MASK_OM) res |= GFC_FPE_OVERFLOW;
+  if (excepts & _FPU_MASK_UM) res |= GFC_FPE_UNDERFLOW;
+  if (excepts & _FPU_MASK_PM) res |= GFC_FPE_INEXACT;
 
-  return result;
+  return res;
 }
 
 void
@@ -414,7 +424,7 @@ get_fpu_rounding_mode (void)
     case _FPU_RC_ZERO:
       return GFC_FPE_TOWARDZERO;
     default:
-      return GFC_FPE_INVALID; /* Should be unreachable.  */
+      return 0; /* Should be unreachable.  */
     }
 }
 
@@ -429,9 +439,6 @@ get_fpu_state (void *state)
 {
   my_fenv_t *envp = state;
 
-  /* Check we can actually store the FPU state in the allocated size.  */
-  assert (sizeof(my_fenv_t) <= (size_t) GFC_FPE_STATE_BUFFER_SIZE);
-
   __asm__ __volatile__ ("fnstenv\t%0" : "=m" (*envp));
 
   /* fnstenv has the side effect of masking all exceptions, so we need
@@ -447,9 +454,6 @@ set_fpu_state (void *state)
 {
   my_fenv_t *envp = state;
 
-  /* Check we can actually store the FPU state in the allocated size.  */
-  assert (sizeof(my_fenv_t) <= (size_t) GFC_FPE_STATE_BUFFER_SIZE);
-
   /* glibc sources (sysdeps/x86_64/fpu/fesetenv.c) do something more
      complex than this, but I think it suffices in our case.  */
   __asm__ __volatile__ ("fldenv\t%0" : : "m" (*envp));
@@ -458,3 +462,47 @@ set_fpu_state (void *state)
     __asm__ __volatile__ ("%vldmxcsr\t%0" : : "m" (envp->__mxcsr));
 }
 
+
+int
+support_fpu_underflow_control (int kind)
+{
+  if (!has_sse())
+    return 0;
+
+  return (kind == 4 || kind == 8) ? 1 : 0;
+}
+
+
+int
+get_fpu_underflow_mode (void)
+{
+  unsigned int cw_sse;
+
+  if (!has_sse())
+    return 1;
+
+  __asm__ __volatile__ ("%vstmxcsr\t%0" : "=m" (cw_sse));
+
+  /* Return 0 for abrupt underflow (flush to zero), 1 for gradual underflow.  */
+  return (cw_sse & MXCSR_FTZ) ? 0 : 1;
+}
+
+
+void
+set_fpu_underflow_mode (int gradual)
+{
+  unsigned int cw_sse;
+
+  if (!has_sse())
+    return;
+
+  __asm__ __volatile__ ("%vstmxcsr\t%0" : "=m" (cw_sse));
+
+  if (gradual)
+    cw_sse &= ~MXCSR_FTZ;
+  else
+    cw_sse |= MXCSR_FTZ;
+
+  __asm__ __volatile__ ("%vldmxcsr\t%0" : : "m" (cw_sse));
+}
+