soft-fp: Add FP_DENORM_ZERO
diff mbox

Message ID Pine.LNX.4.64.1409191817180.11496@digraph.polyomino.org.uk
State New
Headers show

Commit Message

Joseph Myers Sept. 19, 2014, 6:18 p.m. UTC
Continuing the addition of soft-fp features used in the Linux kernel,
this patch adds soft-fp support for FP_DENORM_ZERO (flushing input
subnormal operands to zero of the same sign).  (This patch is relative
to a tree with
<https://sourceware.org/ml/libc-alpha/2014-09/msg00411.html>,
<https://sourceware.org/ml/libc-alpha/2014-09/msg00442.html>,
<https://sourceware.org/ml/libc-alpha/2014-09/msg00461.html>,
<https://sourceware.org/ml/libc-alpha/2014-09/msg00463.html> and
<https://sourceware.org/ml/libc-alpha/2014-09/msg00464.html> applied,
for convenience rather than because of any essential dependency on the
other patches.)

There are some differences from the kernel version.  In the kernel,
the "inexact" exception is set when flushing to zero.  This does not
appear to match the documented semantics for either of the
architectures (alpha and sh) for which the kernel uses FP_DENORM_ZERO,
so this patch does not set "inexact" in this case.  More operations
now use raw or semi-raw unpacking for optimization than did in the
ten-year-old soft-fp version in the kernel, so checks of
FP_DENORM_ZERO are inserted in those operations.  They are also
inserted for comparisons (which already used raw unpacking in the old
version) as I believe that's the correct thing to do when input
subnormals are flushed to zero.  They are *not* inserted for _FP_NEG.
(If any processors do flush input subnormals to zero for negation, or
otherwise vary from the rules implemented when FP_DENORM_ZERO is set,
further macros for sfp-machine.h to control this may need to be
added.)

Although the addition for comparisons will cause FP_EX_DENORM to be
set in this case, it still won't be set for comparisons involving
subnormals when not flushed to zero.  It's quite possible that
accurate emulation of processors that have such an exception for
subnormal operands will require further changes relating to when
FP_EX_DENORM is set (in general, the support for things defined by
IEEE should be considered more reliable and mature than the support
for things outside the scope of IEEE floating point).

Although some processors also have a mode for abrupt underflow -
producing zeroes instead of output subnormals - there is no such mode
in the kernel's soft-fp, so no such mode is added to glibc's soft-fp
(although it could be if someone wanted to emulate such processor
support).

Tested for powerpc-nofpu that the disassembly of installed shared
libraries is unchanged by this patch.

2014-09-19  Joseph Myers  <joseph@codesourcery.com>

	* soft-fp/soft-fp.h (FP_DENORM_ZERO): New macro.
	* soft-fp/op-common.h (_FP_UNPACK_CANONICAL): Check
	FP_DENORM_ZERO.
	(_FP_CHECK_FLUSH_ZERO): New macro.
	(_FP_ADD_INTERNAL): Call _FP_CHECK_FLUSH_ZERO.
	(_FP_CMP): Likewise.
	(_FP_CMP_EQ): Likewise.
	(_FP_TO_INT): Do not set inexact for subnormal arguments if
	FP_DENORM_ZERO.
	(FP_EXTEND): Call _FP_CHECK_FLUSH_ZERO.
	(FP_TRUNC): Likewise.

Comments

Joseph Myers Sept. 26, 2014, 6:19 p.m. UTC | #1
Ping.  This patch 
<https://sourceware.org/ml/libc-alpha/2014-09/msg00488.html> is pending 
review.
Joseph Myers Oct. 2, 2014, 10:20 a.m. UTC | #2
Ping^2.  This patch 
<https://sourceware.org/ml/libc-alpha/2014-09/msg00488.html> is pending 
review.
Carlos O'Donell Oct. 9, 2014, 12:38 a.m. UTC | #3
On 09/19/2014 02:18 PM, Joseph S. Myers wrote:
> Continuing the addition of soft-fp features used in the Linux kernel,
> this patch adds soft-fp support for FP_DENORM_ZERO (flushing input
> subnormal operands to zero of the same sign).  (This patch is relative
> to a tree with
> <https://sourceware.org/ml/libc-alpha/2014-09/msg00411.html>,
> <https://sourceware.org/ml/libc-alpha/2014-09/msg00442.html>,
> <https://sourceware.org/ml/libc-alpha/2014-09/msg00461.html>,
> <https://sourceware.org/ml/libc-alpha/2014-09/msg00463.html> and
> <https://sourceware.org/ml/libc-alpha/2014-09/msg00464.html> applied,
> for convenience rather than because of any essential dependency on the
> other patches.)
> 
> There are some differences from the kernel version.  In the kernel,
> the "inexact" exception is set when flushing to zero.  This does not
> appear to match the documented semantics for either of the
> architectures (alpha and sh) for which the kernel uses FP_DENORM_ZERO,
> so this patch does not set "inexact" in this case.  More operations
> now use raw or semi-raw unpacking for optimization than did in the
> ten-year-old soft-fp version in the kernel, so checks of
> FP_DENORM_ZERO are inserted in those operations.  They are also
> inserted for comparisons (which already used raw unpacking in the old
> version) as I believe that's the correct thing to do when input
> subnormals are flushed to zero.  They are *not* inserted for _FP_NEG.
> (If any processors do flush input subnormals to zero for negation, or
> otherwise vary from the rules implemented when FP_DENORM_ZERO is set,
> further macros for sfp-machine.h to control this may need to be
> added.)
> 
> Although the addition for comparisons will cause FP_EX_DENORM to be
> set in this case, it still won't be set for comparisons involving
> subnormals when not flushed to zero.  It's quite possible that
> accurate emulation of processors that have such an exception for
> subnormal operands will require further changes relating to when
> FP_EX_DENORM is set (in general, the support for things defined by
> IEEE should be considered more reliable and mature than the support
> for things outside the scope of IEEE floating point).
> 
> Although some processors also have a mode for abrupt underflow -
> producing zeroes instead of output subnormals - there is no such mode
> in the kernel's soft-fp, so no such mode is added to glibc's soft-fp
> (although it could be if someone wanted to emulate such processor
> support).
> 
> Tested for powerpc-nofpu that the disassembly of installed shared
> libraries is unchanged by this patch.
> 
> 2014-09-19  Joseph Myers  <joseph@codesourcery.com>
> 
> 	* soft-fp/soft-fp.h (FP_DENORM_ZERO): New macro.
> 	* soft-fp/op-common.h (_FP_UNPACK_CANONICAL): Check
> 	FP_DENORM_ZERO.
> 	(_FP_CHECK_FLUSH_ZERO): New macro.
> 	(_FP_ADD_INTERNAL): Call _FP_CHECK_FLUSH_ZERO.
> 	(_FP_CMP): Likewise.
> 	(_FP_CMP_EQ): Likewise.
> 	(_FP_TO_INT): Do not set inexact for subnormal arguments if
> 	FP_DENORM_ZERO.
> 	(FP_EXTEND): Call _FP_CHECK_FLUSH_ZERO.
> 	(FP_TRUNC): Likewise.

Looks good to me.

Small nit about the "centralized defaults" pattern being typo-prone.

> diff --git a/soft-fp/op-common.h b/soft-fp/op-common.h
> index 18d6e31..3deb9b1 100644
> --- a/soft-fp/op-common.h
> +++ b/soft-fp/op-common.h
> @@ -63,6 +63,12 @@
>  	case 0:							\
>  	  if (_FP_FRAC_ZEROP_##wc (X))				\
>  	    X##_c = FP_CLS_ZERO;				\
> +	  else if (FP_DENORM_ZERO)				\
> +	    {							\
> +	      X##_c = FP_CLS_ZERO;				\
> +	      _FP_FRAC_SET_##wc (X, _FP_ZEROFRAC_##wc);		\
> +	      FP_SET_EXCEPTION (FP_EX_DENORM);			\

OK.

> +	    }							\
>  	  else							\
>  	    {							\
>  	      /* A denormalized number.  */			\
> @@ -100,6 +106,21 @@
>     other classification is not done.  */
>  #define _FP_UNPACK_SEMIRAW(fs, wc, X)	_FP_FRAC_SLL_##wc (X, _FP_WORKBITS)
>  
> +/* Check whether a raw or semi-raw input value should be flushed to
> +   zero, and flush it to zero if so.  */
> +#define _FP_CHECK_FLUSH_ZERO(fs, wc, X)			\
> +  do							\
> +    {							\
> +      if (FP_DENORM_ZERO				\
> +	  && X##_e == 0					\
> +	  && !_FP_FRAC_ZEROP_##wc (X))			\
> +	{						\
> +	  _FP_FRAC_SET_##wc (X, _FP_ZEROFRAC_##wc);	\
> +	  FP_SET_EXCEPTION (FP_EX_DENORM);		\

OK.

> +	}						\
> +    }							\
> +  while (0)
> +
>  /* A semi-raw value has overflowed to infinity.  Adjust the mantissa
>     and exponent appropriately.  */
>  #define _FP_OVERFLOW_SEMIRAW(fs, wc, X)			\
> @@ -388,6 +409,8 @@
>  #define _FP_ADD_INTERNAL(fs, wc, R, X, Y, OP)				\
>    do									\
>      {									\
> +      _FP_CHECK_FLUSH_ZERO (fs, wc, X);					\
> +      _FP_CHECK_FLUSH_ZERO (fs, wc, Y);					\

OK.

>        if (X##_s == Y##_s)						\
>  	{								\
>  	  /* Addition.  */						\
> @@ -1235,6 +1258,9 @@
>  	  int _FP_CMP_is_zero_x;					\
>  	  int _FP_CMP_is_zero_y;					\
>  									\
> +	  _FP_CHECK_FLUSH_ZERO (fs, wc, X);				\
> +	  _FP_CHECK_FLUSH_ZERO (fs, wc, Y);				\

OK.

> +									\
>  	  _FP_CMP_is_zero_x						\
>  	    = (!X##_e && _FP_FRAC_ZEROP_##wc (X)) ? 1 : 0;		\
>  	  _FP_CMP_is_zero_y						\
> @@ -1277,6 +1303,9 @@
>  	}								\
>        else								\
>  	{								\
> +	  _FP_CHECK_FLUSH_ZERO (fs, wc, X);				\
> +	  _FP_CHECK_FLUSH_ZERO (fs, wc, Y);				\

OK.

> +									\
>  	  ret = !(X##_e == Y##_e					\
>  		  && _FP_FRAC_EQ_##wc (X, Y)				\
>  		  && (X##_s == Y##_s || (!X##_e && _FP_FRAC_ZEROP_##wc (X)))); \
> @@ -1374,7 +1403,8 @@
>  	    {								\
>  	      if (!_FP_FRAC_ZEROP_##wc (X))				\
>  		{							\
> -		  FP_SET_EXCEPTION (FP_EX_INEXACT);			\
> +		  if (!FP_DENORM_ZERO)					\
> +		    FP_SET_EXCEPTION (FP_EX_INEXACT);			\

OK.

>  		  FP_SET_EXCEPTION (FP_EX_DENORM);			\
>  		}							\
>  	    }								\
> @@ -1558,6 +1588,7 @@
>  	{								\
>  	  if (S##_e == 0)						\
>  	    {								\
> +	      _FP_CHECK_FLUSH_ZERO (sfs, swc, S);			\

OK.

>  	      if (_FP_FRAC_ZEROP_##swc (S))				\
>  		D##_e = 0;						\
>  	      else if (_FP_EXPBIAS_##dfs				\
> @@ -1644,6 +1675,7 @@
>  	{								\
>  	  if (S##_e == 0)						\
>  	    {								\
> +	      _FP_CHECK_FLUSH_ZERO (sfs, swc, S);			\

OK.

>  	      D##_e = 0;						\
>  	      if (_FP_FRAC_ZEROP_##swc (S))				\
>  		_FP_FRAC_SET_##dwc (D, _FP_ZEROFRAC_##dwc);		\
> diff --git a/soft-fp/soft-fp.h b/soft-fp/soft-fp.h
> index 86bc7fa..05fcca0 100644
> --- a/soft-fp/soft-fp.h
> +++ b/soft-fp/soft-fp.h
> @@ -161,6 +161,11 @@
>  # define FP_HANDLE_EXCEPTIONS do {} while (0)
>  #endif
>  
> +/* Whether to flush subnormal inputs to zero with the same sign.  */
> +#ifndef FP_DENORM_ZERO
> +# define FP_DENORM_ZERO 0
> +#endif

OK, but...

For the record this is an instance of the "centralized defaults" pattern
which we try to avoid in glibc. What if the target accidentally defines:

#define FP_DENORM_ZER 1

or 

#define FP_DENRM_ZERO 1

See:
https://sourceware.org/glibc/wiki/Wundef

> +
>  #ifndef FP_INHIBIT_RESULTS
>  /* By default we write the results always.
>     sfp-machine may override this and e.g.
> 

Cheers,
Carlos.
Joseph Myers Oct. 9, 2014, 12:57 a.m. UTC | #4
On Wed, 8 Oct 2014, Carlos O'Donell wrote:

> > diff --git a/soft-fp/soft-fp.h b/soft-fp/soft-fp.h
> > index 86bc7fa..05fcca0 100644
> > --- a/soft-fp/soft-fp.h
> > +++ b/soft-fp/soft-fp.h
> > @@ -161,6 +161,11 @@
> >  # define FP_HANDLE_EXCEPTIONS do {} while (0)
> >  #endif
> >  
> > +/* Whether to flush subnormal inputs to zero with the same sign.  */
> > +#ifndef FP_DENORM_ZERO
> > +# define FP_DENORM_ZERO 0
> > +#endif
> 
> OK, but...
> 
> For the record this is an instance of the "centralized defaults" pattern
> which we try to avoid in glibc. What if the target accidentally defines:

I am unconvinced this can readily be avoided in the soft-fp code without a 
lot of duplication of common definitions in different sfp-machine.h files 
(of which there are a lot, when you consider those in libgcc and the Linux 
kernel as well as glibc).
Carlos O'Donell Oct. 9, 2014, 1:54 a.m. UTC | #5
On 10/08/2014 08:57 PM, Joseph S. Myers wrote:
> On Wed, 8 Oct 2014, Carlos O'Donell wrote:
> 
>>> diff --git a/soft-fp/soft-fp.h b/soft-fp/soft-fp.h
>>> index 86bc7fa..05fcca0 100644
>>> --- a/soft-fp/soft-fp.h
>>> +++ b/soft-fp/soft-fp.h
>>> @@ -161,6 +161,11 @@
>>>  # define FP_HANDLE_EXCEPTIONS do {} while (0)
>>>  #endif
>>>  
>>> +/* Whether to flush subnormal inputs to zero with the same sign.  */
>>> +#ifndef FP_DENORM_ZERO
>>> +# define FP_DENORM_ZERO 0
>>> +#endif
>>
>> OK, but...
>>
>> For the record this is an instance of the "centralized defaults" pattern
>> which we try to avoid in glibc. What if the target accidentally defines:
> 
> I am unconvinced this can readily be avoided in the soft-fp code without a 
> lot of duplication of common definitions in different sfp-machine.h files 
> (of which there are a lot, when you consider those in libgcc and the Linux 
> kernel as well as glibc).

I am not sufficiently familiar with the code to make that judgement, I was
only noting that we're trying to avoid that kind of pattern in glibc in
general. However, "in general" may not apply to this specific case for
soft-fp.

Cheers,
Carlos.

Patch
diff mbox

diff --git a/soft-fp/op-common.h b/soft-fp/op-common.h
index 18d6e31..3deb9b1 100644
--- a/soft-fp/op-common.h
+++ b/soft-fp/op-common.h
@@ -63,6 +63,12 @@ 
 	case 0:							\
 	  if (_FP_FRAC_ZEROP_##wc (X))				\
 	    X##_c = FP_CLS_ZERO;				\
+	  else if (FP_DENORM_ZERO)				\
+	    {							\
+	      X##_c = FP_CLS_ZERO;				\
+	      _FP_FRAC_SET_##wc (X, _FP_ZEROFRAC_##wc);		\
+	      FP_SET_EXCEPTION (FP_EX_DENORM);			\
+	    }							\
 	  else							\
 	    {							\
 	      /* A denormalized number.  */			\
@@ -100,6 +106,21 @@ 
    other classification is not done.  */
 #define _FP_UNPACK_SEMIRAW(fs, wc, X)	_FP_FRAC_SLL_##wc (X, _FP_WORKBITS)
 
+/* Check whether a raw or semi-raw input value should be flushed to
+   zero, and flush it to zero if so.  */
+#define _FP_CHECK_FLUSH_ZERO(fs, wc, X)			\
+  do							\
+    {							\
+      if (FP_DENORM_ZERO				\
+	  && X##_e == 0					\
+	  && !_FP_FRAC_ZEROP_##wc (X))			\
+	{						\
+	  _FP_FRAC_SET_##wc (X, _FP_ZEROFRAC_##wc);	\
+	  FP_SET_EXCEPTION (FP_EX_DENORM);		\
+	}						\
+    }							\
+  while (0)
+
 /* A semi-raw value has overflowed to infinity.  Adjust the mantissa
    and exponent appropriately.  */
 #define _FP_OVERFLOW_SEMIRAW(fs, wc, X)			\
@@ -388,6 +409,8 @@ 
 #define _FP_ADD_INTERNAL(fs, wc, R, X, Y, OP)				\
   do									\
     {									\
+      _FP_CHECK_FLUSH_ZERO (fs, wc, X);					\
+      _FP_CHECK_FLUSH_ZERO (fs, wc, Y);					\
       if (X##_s == Y##_s)						\
 	{								\
 	  /* Addition.  */						\
@@ -1235,6 +1258,9 @@ 
 	  int _FP_CMP_is_zero_x;					\
 	  int _FP_CMP_is_zero_y;					\
 									\
+	  _FP_CHECK_FLUSH_ZERO (fs, wc, X);				\
+	  _FP_CHECK_FLUSH_ZERO (fs, wc, Y);				\
+									\
 	  _FP_CMP_is_zero_x						\
 	    = (!X##_e && _FP_FRAC_ZEROP_##wc (X)) ? 1 : 0;		\
 	  _FP_CMP_is_zero_y						\
@@ -1277,6 +1303,9 @@ 
 	}								\
       else								\
 	{								\
+	  _FP_CHECK_FLUSH_ZERO (fs, wc, X);				\
+	  _FP_CHECK_FLUSH_ZERO (fs, wc, Y);				\
+									\
 	  ret = !(X##_e == Y##_e					\
 		  && _FP_FRAC_EQ_##wc (X, Y)				\
 		  && (X##_s == Y##_s || (!X##_e && _FP_FRAC_ZEROP_##wc (X)))); \
@@ -1374,7 +1403,8 @@ 
 	    {								\
 	      if (!_FP_FRAC_ZEROP_##wc (X))				\
 		{							\
-		  FP_SET_EXCEPTION (FP_EX_INEXACT);			\
+		  if (!FP_DENORM_ZERO)					\
+		    FP_SET_EXCEPTION (FP_EX_INEXACT);			\
 		  FP_SET_EXCEPTION (FP_EX_DENORM);			\
 		}							\
 	    }								\
@@ -1558,6 +1588,7 @@ 
 	{								\
 	  if (S##_e == 0)						\
 	    {								\
+	      _FP_CHECK_FLUSH_ZERO (sfs, swc, S);			\
 	      if (_FP_FRAC_ZEROP_##swc (S))				\
 		D##_e = 0;						\
 	      else if (_FP_EXPBIAS_##dfs				\
@@ -1644,6 +1675,7 @@ 
 	{								\
 	  if (S##_e == 0)						\
 	    {								\
+	      _FP_CHECK_FLUSH_ZERO (sfs, swc, S);			\
 	      D##_e = 0;						\
 	      if (_FP_FRAC_ZEROP_##swc (S))				\
 		_FP_FRAC_SET_##dwc (D, _FP_ZEROFRAC_##dwc);		\
diff --git a/soft-fp/soft-fp.h b/soft-fp/soft-fp.h
index 86bc7fa..05fcca0 100644
--- a/soft-fp/soft-fp.h
+++ b/soft-fp/soft-fp.h
@@ -161,6 +161,11 @@ 
 # define FP_HANDLE_EXCEPTIONS do {} while (0)
 #endif
 
+/* Whether to flush subnormal inputs to zero with the same sign.  */
+#ifndef FP_DENORM_ZERO
+# define FP_DENORM_ZERO 0
+#endif
+
 #ifndef FP_INHIBIT_RESULTS
 /* By default we write the results always.
    sfp-machine may override this and e.g.