Patchwork [libgcc] : Put soft-FP exception handler out-of-line for x86

login
register
mail settings
Submitter Uros Bizjak
Date June 9, 2012, 5:34 p.m.
Message ID <CAFULd4bjYagcKcVs_PcFmfbsx5kShFHPD+QU3SYA80k07=ELpA@mail.gmail.com>
Download mbox | patch
Permalink /patch/163945/
State New
Headers show

Comments

Uros Bizjak - June 9, 2012, 5:34 p.m.
On Thu, Jun 7, 2012 at 9:58 PM, Uros Bizjak <ubizjak@gmail.com> wrote:

> Attached patch rewrites x86 soft-FP as an out-of-line function that
> gets conditionally called when exception occurs. This rewrite removes
> 17 instances of the same code from libgcc. In addition, the patch
> unifies a lot of code between 32bit and 64bit x86 target, and also
> introduces SSE instructions for 32bit targets. There is no other
> functional changes, although the change to real SSE FP ops is
> tempting. ;)
>
> 2012-06-07  Uros Bizjak  <ubizjak@gmail.com>
>
>        * config/i386/32/sfp-machine.h (__gcc_CMPtype, CMPtype,
>        _FP_KEEPNANFRACP, _FP_CHOOSENAN, FP_EX_INVALID, FP_EX_DENORM,
>        FP_EX_DIVZERO, FP_EX_OVERFLOW, FP_EX_UNDERFLOW, FP_EX_INEXACT,
>        FP_HANDLE_EXCEPTIONS, FP_RND_NEAREST, FP_RND_ZERO, FP_RND_PINF,
>        FP_RND_MINF, _FP_DEXL_EX, FP_INIT_ROUNDMODE, FP_ROUNDMODE,
>        __LITTLE_ENDIAN, __BIG_ENDIAN, strong_alias): Move ...
>        * config/i386/64/sfp-machine: ... (delete here) ...
>        * config/i386/sfp-machine.h: ... to here.
>        (FP_EX_MASK): New.
>        (__sfp_handle_exceptions): New function declaration.
>        (FP_HANDLE_EXCEPTIONS): Use __sfp_handle_exceptions.
>        * config/i386/sfp-exceptions.c: New.
>        * config/i386/t-softfp: New.
>        * config.host (i[34567]86-*-* and x86_64-*-* soft-fp targets): Add
>        i386/t-softfp to tmake_file.
>
> Patch was tested on x86_64-pc-linux-gnu {,-m32}.

I have committed attached, slightly changed patch (asm patterns in
__sfp_handle_exceptions) to the mainline SVN.

Uros.

Patch

Index: config/i386/sfp-machine.h
===================================================================
--- config/i386/sfp-machine.h	(revision 188333)
+++ config/i386/sfp-machine.h	(working copy)
@@ -3,8 +3,83 @@ 
 #define _FP_STRUCT_LAYOUT  __attribute__ ((gcc_struct))
 #endif
 
+/* The type of the result of a floating point comparison.  This must
+   match `__libgcc_cmp_return__' in GCC for the target.  */
+typedef int __gcc_CMPtype __attribute__ ((mode (__libgcc_cmp_return__)));
+#define CMPtype __gcc_CMPtype
+
 #ifdef __x86_64__
 #include "config/i386/64/sfp-machine.h"
 #else
 #include "config/i386/32/sfp-machine.h"
 #endif
+
+#define _FP_KEEPNANFRACP 1
+
+/* Here is something Intel misdesigned: the specs don't define
+   the case where we have two NaNs with same mantissas, but
+   different sign. Different operations pick up different NaNs.  */
+#define _FP_CHOOSENAN(fs, wc, R, X, Y, OP)			\
+  do {								\
+    if (_FP_FRAC_GT_##wc(X, Y)					\
+	|| (_FP_FRAC_EQ_##wc(X,Y) && (OP == '+' || OP == '*')))	\
+      {								\
+	R##_s = X##_s;						\
+        _FP_FRAC_COPY_##wc(R,X);				\
+      }								\
+    else							\
+      {								\
+	R##_s = Y##_s;						\
+        _FP_FRAC_COPY_##wc(R,Y);				\
+      }								\
+    R##_c = FP_CLS_NAN;						\
+  } while (0)
+
+#define FP_EX_INVALID		0x01
+#define FP_EX_DENORM		0x02
+#define FP_EX_DIVZERO		0x04
+#define FP_EX_OVERFLOW		0x08
+#define FP_EX_UNDERFLOW		0x10
+#define FP_EX_INEXACT		0x20
+
+#define FP_EX_MASK		0x3f
+
+void __sfp_handle_exceptions (int);
+
+#define FP_HANDLE_EXCEPTIONS			\
+  do {						\
+    if (_fex & FP_EX_MASK)			\
+      __sfp_handle_exceptions (_fex);		\
+  } while (0);
+
+#define FP_RND_NEAREST		0
+#define FP_RND_ZERO		0xc00
+#define FP_RND_PINF		0x800
+#define FP_RND_MINF		0x400
+
+#define _FP_DECL_EX \
+  unsigned short _fcw __attribute__ ((unused)) = FP_RND_NEAREST
+
+#define FP_INIT_ROUNDMODE			\
+  do {						\
+    __asm__ ("fnstcw %0" : "=m" (_fcw));	\
+  } while (0)
+
+#define FP_ROUNDMODE		(_fcw & 0xc00)
+
+#define	__LITTLE_ENDIAN	1234
+#define	__BIG_ENDIAN	4321
+
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+/* Define ALIASNAME as a strong alias for NAME.  */
+#if defined __MACH__
+/* Mach-O doesn't support aliasing.  If these functions ever return
+   anything but CMPtype we need to revisit this... */
+#define strong_alias(name, aliasname) \
+  CMPtype aliasname (TFtype a, TFtype b) { return name(a, b); }
+#else
+# define strong_alias(name, aliasname) _strong_alias(name, aliasname)
+# define _strong_alias(name, aliasname) \
+  extern __typeof (name) aliasname __attribute__ ((alias (#name)));
+#endif
Index: config/i386/64/sfp-machine.h
===================================================================
--- config/i386/64/sfp-machine.h	(revision 188333)
+++ config/i386/64/sfp-machine.h	(working copy)
@@ -1,5 +1,4 @@ 
 #define _FP_W_TYPE_SIZE		64
-
 #define _FP_W_TYPE		unsigned long long
 #define _FP_WS_TYPE		signed long long
 #define _FP_I_TYPE		long long
@@ -9,11 +8,6 @@ 
 
 #define TI_BITS (__CHAR_BIT__ * (int)sizeof(TItype))
 
-/* The type of the result of a floating point comparison.  This must
-   match `__libgcc_cmp_return__' in GCC for the target.  */
-typedef int __gcc_CMPtype __attribute__ ((mode (__libgcc_cmp_return__)));
-#define CMPtype __gcc_CMPtype
-
 #define _FP_MUL_MEAT_Q(R,X,Y)                           \
   _FP_MUL_MEAT_2_wide(_FP_WFRACBITS_Q,R,X,Y,umul_ppmm)
 
@@ -27,126 +21,3 @@ 
 #define _FP_NANSIGN_D		1
 #define _FP_NANSIGN_E		1
 #define _FP_NANSIGN_Q		1
-
-#define _FP_KEEPNANFRACP 1
-
-/* Here is something Intel misdesigned: the specs don't define
-   the case where we have two NaNs with same mantissas, but
-   different sign. Different operations pick up different NaNs.  */
-#define _FP_CHOOSENAN(fs, wc, R, X, Y, OP)			\
-  do {								\
-    if (_FP_FRAC_GT_##wc(X, Y)					\
-	|| (_FP_FRAC_EQ_##wc(X,Y) && (OP == '+' || OP == '*')))	\
-      {								\
-	R##_s = X##_s;						\
-        _FP_FRAC_COPY_##wc(R,X);				\
-      }								\
-    else							\
-      {								\
-	R##_s = Y##_s;						\
-        _FP_FRAC_COPY_##wc(R,Y);				\
-      }								\
-    R##_c = FP_CLS_NAN;						\
-  } while (0)
-
-#define FP_EX_INVALID		0x01
-#define FP_EX_DENORM		0x02
-#define FP_EX_DIVZERO		0x04
-#define FP_EX_OVERFLOW		0x08
-#define FP_EX_UNDERFLOW		0x10
-#define FP_EX_INEXACT		0x20
-
-struct fenv
-{
-  unsigned short int __control_word;
-  unsigned short int __unused1;
-  unsigned short int __status_word;
-  unsigned short int __unused2;
-  unsigned short int __tags;
-  unsigned short int __unused3;
-  unsigned int __eip;
-  unsigned short int __cs_selector;
-  unsigned int __opcode:11;
-  unsigned int __unused4:5;
-  unsigned int __data_offset;
-  unsigned short int __data_selector;
-  unsigned short int __unused5;
-};
-
-#ifdef __AVX__
- #define ASM_INVALID "vdivss %0, %0, %0"
- #define ASM_DIVZERO "vdivss %1, %0, %0"
-#else
- #define ASM_INVALID "divss %0, %0"
- #define ASM_DIVZERO "divss %1, %0"
-#endif
-
-#define FP_HANDLE_EXCEPTIONS						\
-  do {									\
-    if (_fex & FP_EX_INVALID)						\
-      {									\
-	float f = 0.0;							\
-	__asm__ __volatile__ (ASM_INVALID : : "x" (f));			\
-      }									\
-    if (_fex & FP_EX_DIVZERO)						\
-      {									\
-	float f = 1.0, g = 0.0;						\
-	__asm__ __volatile__ (ASM_DIVZERO : : "x" (f), "x" (g));	\
-      }									\
-    if (_fex & FP_EX_OVERFLOW)						\
-      {									\
-	struct fenv temp;						\
-	__asm__ __volatile__ ("fnstenv %0" : "=m" (temp));		\
-	temp.__status_word |= FP_EX_OVERFLOW;				\
-	__asm__ __volatile__ ("fldenv %0" : : "m" (temp));		\
-	__asm__ __volatile__ ("fwait");					\
-      }									\
-    if (_fex & FP_EX_UNDERFLOW)						\
-      {									\
-	struct fenv temp;						\
-	__asm__ __volatile__ ("fnstenv %0" : "=m" (temp));		\
-	temp.__status_word |= FP_EX_UNDERFLOW;				\
-	__asm__ __volatile__ ("fldenv %0" : : "m" (temp));		\
-	__asm__ __volatile__ ("fwait");					\
-      }									\
-    if (_fex & FP_EX_INEXACT)						\
-      {									\
-	struct fenv temp;						\
-	__asm__ __volatile__ ("fnstenv %0" : "=m" (temp));		\
-	temp.__status_word |= FP_EX_INEXACT;				\
-	__asm__ __volatile__ ("fldenv %0" : : "m" (temp));		\
-	__asm__ __volatile__ ("fwait");					\
-      }									\
-  } while (0)
-
-#define FP_RND_NEAREST		0
-#define FP_RND_ZERO		0xc00
-#define FP_RND_PINF		0x800
-#define FP_RND_MINF		0x400
-
-#define _FP_DECL_EX \
-  unsigned short _fcw __attribute__ ((unused)) = FP_RND_NEAREST
-
-#define FP_INIT_ROUNDMODE			\
-  do {						\
-    __asm__ ("fnstcw %0" : "=m" (_fcw));	\
-  } while (0)
-
-#define FP_ROUNDMODE		(_fcw & 0xc00)
-
-#define	__LITTLE_ENDIAN	1234
-#define	__BIG_ENDIAN	4321
-
-#define __BYTE_ORDER __LITTLE_ENDIAN
-
-/* Define ALIASNAME as a strong alias for NAME.  */
-#if defined __MACH__
-/* Mach-O doesn't support aliasing.  If these functions ever return
-   anything but CMPtype we need to revisit this... */
-#define strong_alias(name, aliasname) \
-  CMPtype aliasname (TFtype a, TFtype b) { return name(a, b); }
-#else
-# define strong_alias(name, aliasname) _strong_alias(name, aliasname)
-# define _strong_alias(name, aliasname) \
-  extern __typeof (name) aliasname __attribute__ ((alias (#name)));
-#endif
Index: config/i386/32/sfp-machine.h
===================================================================
--- config/i386/32/sfp-machine.h	(revision 188333)
+++ config/i386/32/sfp-machine.h	(working copy)
@@ -3,11 +3,6 @@ 
 #define _FP_WS_TYPE		signed int
 #define _FP_I_TYPE		int
 
-/* The type of the result of a floating point comparison.  This must
-   match `__libgcc_cmp_return__' in GCC for the target.  */
-typedef int __gcc_CMPtype __attribute__ ((mode (__libgcc_cmp_return__)));
-#define CMPtype __gcc_CMPtype
-
 #define __FP_FRAC_ADD_4(r3,r2,r1,r0,x3,x2,x1,x0,y3,y2,y1,y0)	\
   __asm__ ("add{l} {%11,%3|%3,%11}\n\t"				\
 	   "adc{l} {%9,%2|%2,%9}\n\t"				\
@@ -85,122 +80,3 @@ 
 #define _FP_NANSIGN_D		1
 #define _FP_NANSIGN_E		1
 #define _FP_NANSIGN_Q		1
-
-#define _FP_KEEPNANFRACP 1
-
-/* Here is something Intel misdesigned: the specs don't define
-   the case where we have two NaNs with same mantissas, but
-   different sign. Different operations pick up different NaNs.  */
-#define _FP_CHOOSENAN(fs, wc, R, X, Y, OP)			\
-  do {								\
-    if (_FP_FRAC_GT_##wc(X, Y)					\
-	|| (_FP_FRAC_EQ_##wc(X,Y) && (OP == '+' || OP == '*')))	\
-      {								\
-	R##_s = X##_s;						\
-        _FP_FRAC_COPY_##wc(R,X);				\
-      }								\
-    else							\
-      {								\
-	R##_s = Y##_s;						\
-        _FP_FRAC_COPY_##wc(R,Y);				\
-      }								\
-    R##_c = FP_CLS_NAN;						\
-  } while (0)
-
-#define FP_EX_INVALID		0x01
-#define FP_EX_DENORM		0x02
-#define FP_EX_DIVZERO		0x04
-#define FP_EX_OVERFLOW		0x08
-#define FP_EX_UNDERFLOW		0x10
-#define FP_EX_INEXACT		0x20
-
-struct fenv
-{
-  unsigned short int __control_word;
-  unsigned short int __unused1;
-  unsigned short int __status_word;
-  unsigned short int __unused2;
-  unsigned short int __tags;
-  unsigned short int __unused3;
-  unsigned int __eip;
-  unsigned short int __cs_selector;
-  unsigned int __opcode:11;
-  unsigned int __unused4:5;
-  unsigned int __data_offset;
-  unsigned short int __data_selector;
-  unsigned short int __unused5;
-};
-
-#define FP_HANDLE_EXCEPTIONS						\
-  do {									\
-    if (_fex & FP_EX_INVALID)						\
-      {									\
-	float f = 0.0;							\
-	__asm__ __volatile__ ("fdiv {%y0, %0|%0, %y0}" : "+t" (f));	\
-	__asm__ __volatile__ ("fwait");					\
-      }									\
-    if (_fex & FP_EX_DIVZERO)						\
-      {									\
-	float f = 1.0, g = 0.0;						\
-	__asm__ __volatile__ ("fdivp {%0, %y1|%y1, %0}"			\
-				      : "+t" (f) : "u" (g)		\
-				      : "st(1)");			\
-	__asm__ __volatile__ ("fwait");					\
-      }									\
-    if (_fex & FP_EX_OVERFLOW)						\
-      {									\
-	struct fenv temp;						\
-	__asm__ __volatile__ ("fnstenv %0" : "=m" (temp));		\
-	temp.__status_word |= FP_EX_OVERFLOW;				\
-	__asm__ __volatile__ ("fldenv %0" : : "m" (temp));		\
-	__asm__ __volatile__ ("fwait");					\
-      }									\
-    if (_fex & FP_EX_UNDERFLOW)						\
-      {									\
-	struct fenv temp;						\
-	__asm__ __volatile__ ("fnstenv %0" : "=m" (temp));		\
-	temp.__status_word |= FP_EX_UNDERFLOW;				\
-	__asm__ __volatile__ ("fldenv %0" : : "m" (temp));		\
-	__asm__ __volatile__ ("fwait");					\
-      }									\
-    if (_fex & FP_EX_INEXACT)						\
-      {									\
-	struct fenv temp;						\
-	__asm__ __volatile__ ("fnstenv %0" : "=m" (temp));		\
-	temp.__status_word |= FP_EX_INEXACT;				\
-	__asm__ __volatile__ ("fldenv %0" : : "m" (temp));		\
-	__asm__ __volatile__ ("fwait");					\
-      }									\
-  } while (0)
-
-#define FP_RND_NEAREST		0
-#define FP_RND_ZERO		0xc00
-#define FP_RND_PINF		0x800
-#define FP_RND_MINF		0x400
-
-#define _FP_DECL_EX \
-  unsigned short _fcw __attribute__ ((unused)) = FP_RND_NEAREST
-
-#define FP_INIT_ROUNDMODE			\
-  do {						\
-    __asm__ ("fnstcw %0" : "=m" (_fcw));	\
-  } while (0)
-
-#define FP_ROUNDMODE		(_fcw & 0xc00)
-
-#define	__LITTLE_ENDIAN	1234
-#define	__BIG_ENDIAN	4321
-
-#define __BYTE_ORDER __LITTLE_ENDIAN
-
-/* Define ALIASNAME as a strong alias for NAME.  */
-#if defined __MACH__
-/* Mach-O doesn't support aliasing.  If these functions ever return
-   anything but CMPtype we need to revisit this... */
-#define strong_alias(name, aliasname) \
-  CMPtype aliasname (TFtype a, TFtype b) { return name(a, b); }
-#else
-# define strong_alias(name, aliasname) _strong_alias(name, aliasname)
-# define _strong_alias(name, aliasname) \
-  extern __typeof (name) aliasname __attribute__ ((alias (#name)));
-#endif
Index: config/i386/t-softfp
===================================================================
--- config/i386/t-softfp	(revision 0)
+++ config/i386/t-softfp	(working copy)
@@ -0,0 +1 @@ 
+LIB2ADD += $(srcdir)/config/i386/sfp-exceptions.c
Index: config/i386/sfp-exceptions.c
===================================================================
--- config/i386/sfp-exceptions.c	(revision 0)
+++ config/i386/sfp-exceptions.c	(working copy)
@@ -0,0 +1,90 @@ 
+/*
+ * Copyright (C) 2012 Free Software Foundation, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 3, or (at your option) any
+ * later version.
+ * 
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * Under Section 7 of GPL version 3, you are granted additional
+ * permissions described in the GCC Runtime Library Exception, version
+ * 3.1, as published by the Free Software Foundation.
+ *
+ * You should have received a copy of the GNU General Public License and
+ * 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 "sfp-machine.h"
+
+struct fenv
+{
+  unsigned short int __control_word;
+  unsigned short int __unused1;
+  unsigned short int __status_word;
+  unsigned short int __unused2;
+  unsigned short int __tags;
+  unsigned short int __unused3;
+  unsigned int __eip;
+  unsigned short int __cs_selector;
+  unsigned int __opcode:11;
+  unsigned int __unused4:5;
+  unsigned int __data_offset;
+  unsigned short int __data_selector;
+  unsigned short int __unused5;
+};
+
+void
+__sfp_handle_exceptions (int _fex)
+{
+  if (_fex & FP_EX_INVALID)
+    {
+      float f = 0.0f;
+#ifdef __SSE__
+      asm volatile ("%vdivss\t{%0, %d0|%d0, %0}" : "+x" (f));
+#else
+      asm volatile ("fdiv\t{%y0, %0|%0, %y0}" : "+t" (f));
+      asm volatile ("fwait");
+#endif
+    }
+  if (_fex & FP_EX_DIVZERO)
+    {
+      float f = 1.0f, g = 0.0f;
+#ifdef __SSE__
+      asm volatile ("%vdivss\t{%1, %d0|%d0, %1}" : "+x" (f) : "xm" (g));
+#else
+      asm volatile ("fdivs\t%1" : "+t" (f) : "m" (g));
+      asm volatile ("fwait");
+#endif
+    }
+  if (_fex & FP_EX_OVERFLOW)
+    {
+      struct fenv temp;
+      asm volatile ("fnstenv\t%0" : "=m" (temp));
+      temp.__status_word |= FP_EX_OVERFLOW;
+      asm volatile ("fldenv\t%0" : : "m" (temp));
+      asm volatile ("fwait");
+    }
+  if (_fex & FP_EX_UNDERFLOW)
+    {
+      struct fenv temp;
+      asm volatile ("fnstenv\t%0" : "=m" (temp));
+      temp.__status_word |= FP_EX_UNDERFLOW;
+      asm volatile ("fldenv\t%0" : : "m" (temp));
+      asm volatile ("fwait");
+    }
+  if (_fex & FP_EX_INEXACT)
+    {
+      struct fenv temp;
+      asm volatile ("fnstenv\t%0" : "=m" (temp));
+      temp.__status_word |= FP_EX_INEXACT;
+      asm volatile ("fldenv\t%0" : : "m" (temp));
+      asm volatile ("fwait");
+    }
+};
Index: config.host
===================================================================
--- config.host	(revision 188333)
+++ config.host	(working copy)
@@ -1153,7 +1153,7 @@ 
 	if test "${host_address}" = 32; then
 		tmake_file="${tmake_file} i386/${host_address}/t-softfp"
 	fi
-	tmake_file="${tmake_file} t-softfp"
+	tmake_file="${tmake_file} i386/t-softfp t-softfp"
 	;;
 esac