diff mbox

[Fortran] PR35862 - add input I/O rounding support - by setting the CPU rounding mode

Message ID CAFULd4Z+EnQHgxBJcCp9sTOBywC_F=xdVt+Z26qsQPYgjXJFrw@mail.gmail.com
State New
Headers show

Commit Message

Uros Bizjak July 17, 2013, 9:29 a.m. UTC
On Wed, Jul 17, 2013 at 11:02 AM, Tobias Burnus <burnus@net-b.de> wrote:
> As there is again a CPU dependence:
> - David, can you have a look at config/fpu-aix.h?
> - Eric and Gerald, can you have a look at config/fpu-sysv.h?
> - Uros, can you have a look at config/fpu-387.h?

Please use attached (untested for fortran) patch for fpu-387.h.

Uros.
diff mbox

Patch

Index: libgfortran/config/fpu-387.h
===================================================================
--- libgfortran/config/fpu-387.h	(revision 200979)
+++ libgfortran/config/fpu-387.h	(working copy)
@@ -88,7 +88,7 @@ 
 #endif
 }
 
-/* i387 -- see linux <fpu_control.h> header file for details.  */
+/* i387 exceptions -- see linux <fpu_control.h> header file for details.  */
 #define _FPU_MASK_IM  0x01
 #define _FPU_MASK_DM  0x02
 #define _FPU_MASK_ZM  0x04
@@ -99,7 +99,18 @@ 
 
 #define _FPU_EX_ALL   0x3f
 
-void set_fpu (void)
+/* i387 rounding modes.  */
+
+#define _FPU_RC_NEAREST 0x0
+#define _FPU_RC_DOWN    0x400
+#define _FPU_RC_UP      0x800
+#define _FPU_RC_ZERO    0xc00
+
+#define _FPU_RC_MASK    0xc00
+
+
+void
+set_fpu (void)
 {
   int excepts = 0;
   unsigned short cw;
@@ -164,3 +175,72 @@ 
 
   return result;
 }
+
+void
+set_fpu_rounding_mode (int round)
+{
+  int round_mode;
+  unsigned short cw;
+
+  switch (round)
+    {
+    case GFC_FPE_TONEAREST:
+      round_mode = _FPU_RC_NEAREST;
+      break;
+    case GFC_FPE_UPWARD:
+      round_mode = _FPU_RC_UP;
+      break;
+    case GFC_FPE_DOWNWARD:
+      round_mode = _FPU_RC_DOWN;
+      break;
+    case GFC_FPE_TOWARDZERO:
+      round_mode = _FPU_RC_ZERO;
+      break;
+    default:
+      return; /* Should be unreachable.  */
+    }
+
+  __asm__ __volatile__ ("fnstcw\t%0" : "=m" (cw));
+
+  cw &= ~FPU_RC_MASK;
+  cw |= round_mode;
+
+  __asm__ __volatile__ ("fldcw\t%0" : : "m" (cw));
+
+  if (has_sse())
+    {
+      unsigned int cw_sse;
+
+      __asm__ __volatile__ ("%vstmxcsr\t%0" : "=m" (cw_sse));
+
+      /* The SSE round control bits are shifted by 3 bits.  */
+      cw_sse &= ~(FPU_RC_MASK << 3);
+      cw_sse |= round_mode << 3;
+
+      __asm__ __volatile__ ("%vldmxcsr\t%0" : : "m" (cw_sse));
+    }
+}
+
+int
+get_fpu_rounding_mode (void)
+{
+  unsigned short cw;
+
+  __asm__ __volatile__ ("fnstcw\t%0" : "=m" (cw));
+
+  cw &= FPU_RC_MASK;
+
+  switch (cw)
+    {
+    case _FPU_RC_NEAREST:
+      return GFC_FPE_TONEAREST;
+    case _FPU_RC_UP:
+      return GFC_FPE_UPWARD;
+    case _FPU_RC_DOWN:
+      return GFC_FPE_DOWNWARD;
+    case _FPU_RC_ZERO:
+      return GFC_FPE_TOWARDZERO;
+    default:
+      return GFC_FPE_INVALID; /* Should be unreachable.  */
+    }
+}