soft-fp: Add FP_DENORM_ZERO

Carlos O'Donell carlos@redhat.com
Thu Oct 9 00:38:00 GMT 2014


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.



More information about the Libc-alpha mailing list