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

login
register
mail settings
Submitter Uros Bizjak
Date June 7, 2012, 7:58 p.m.
Message ID <CAFULd4Y83EQhqCxoh-j2iabSEHim_hM6SDtrg2t-fXNWrpz8JQ@mail.gmail.com>
Download mbox | patch
Permalink /patch/163665/
State New
Headers show

Comments

Uros Bizjak - June 7, 2012, 7:58 p.m.
Hello!

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}.

The patch needs approval from libgcc maintainer.

OK for mainline?

Uros.

Patch

Index: config/i386/sfp-machine.h
===================================================================
--- config/i386/sfp-machine.h	(revision 188308)
+++ 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/32/sfp-machine.h
===================================================================
--- config/i386/32/sfp-machine.h	(revision 188308)
+++ 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/sfp-exceptions.c
===================================================================
--- config/i386/sfp-exceptions.c	(revision 0)
+++ config/i386/sfp-exceptions.c	(working copy)
@@ -0,0 +1,100 @@ 
+/*
+ * 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;
+};
+
+#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
+
+void
+__sfp_handle_exceptions (int _fex)
+{
+  if (_fex & FP_EX_INVALID)
+    {
+      float f = 0.0f;
+#ifdef __SSE__
+      asm volatile (ASM_INVALID : : "x" (f));
+#else
+      asm volatile ("fdiv {%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 (ASM_DIVZERO : : "x" (f), "x" (g));
+#else
+      asm volatile ("fdivp {%0, %y1|%y1, %0}"
+			    : "+t" (f) : "u" (g)
+			    : "st(1)");
+      asm volatile ("fwait");
+#endif
+    }
+  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");
+    }
+};
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/64/sfp-machine.h
===================================================================
--- config/i386/64/sfp-machine.h	(revision 188308)
+++ 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.host
===================================================================
--- config.host	(revision 188308)
+++ 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