Patchwork [1/2] softfloat: Implement flushing input denormals to zero

login
register
mail settings
Submitter Peter Maydell
Date Jan. 3, 2011, 4:48 p.m.
Message ID <1294073334-2373-2-git-send-email-peter.maydell@linaro.org>
Download mbox | patch
Permalink /patch/77305/
State New
Headers show

Comments

Peter Maydell - Jan. 3, 2011, 4:48 p.m.
Add support to softfloat for flushing input denormal float32 and float64
to zero. softfloat's existing 'flush_to_zero' flag only flushes denormals
to zero on output. Some CPUs need input denormals to be flushed before
processing as well. Implement this, using a new status flag to enable it
and a new exception status bit to indicate when it has happened. Existing
CPUs should be unaffected as there is no behaviour change unless the
mode is enabled.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 fpu/softfloat.c |  104 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 fpu/softfloat.h |   22 +++++++++++-
 2 files changed, 123 insertions(+), 3 deletions(-)
Aurelien Jarno - Jan. 4, 2011, 3:55 p.m.
On Mon, Jan 03, 2011 at 04:48:53PM +0000, Peter Maydell wrote:
> Add support to softfloat for flushing input denormal float32 and float64
> to zero. softfloat's existing 'flush_to_zero' flag only flushes denormals
> to zero on output. Some CPUs need input denormals to be flushed before
> processing as well. Implement this, using a new status flag to enable it
> and a new exception status bit to indicate when it has happened. Existing
> CPUs should be unaffected as there is no behaviour change unless the
> mode is enabled.
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Acked-by: Aurelien Jarno <aurelien@aurel32.net>

> ---
>  fpu/softfloat.c |  104 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  fpu/softfloat.h |   22 +++++++++++-
>  2 files changed, 123 insertions(+), 3 deletions(-)
> 
> diff --git a/fpu/softfloat.c b/fpu/softfloat.c
> index 6f5b05d..17842f4 100644
> --- a/fpu/softfloat.c
> +++ b/fpu/softfloat.c
> @@ -30,8 +30,6 @@ these four paragraphs for those parts of this code that are retained.
>  
>  =============================================================================*/
>  
> -/* FIXME: Flush-To-Zero only effects results.  Denormal inputs should also
> -   be flushed to zero.  */
>  #include "softfloat.h"
>  
>  /*----------------------------------------------------------------------------
> @@ -204,6 +202,21 @@ INLINE flag extractFloat32Sign( float32 a )
>  }
>  
>  /*----------------------------------------------------------------------------
> +| If `a' is denormal and we are in flush-to-zero mode then set the
> +| input-denormal exception and return zero. Otherwise just return the value.
> +*----------------------------------------------------------------------------*/
> +static float32 float32_squash_input_denormal(float32 a STATUS_PARAM)
> +{
> +    if (STATUS(flush_inputs_to_zero)) {
> +        if (extractFloat32Exp(a) == 0 && extractFloat32Frac(a) != 0) {
> +            float_raise(float_flag_input_denormal STATUS_VAR);
> +            return make_float32(float32_val(a) & 0x80000000);
> +        }
> +    }
> +    return a;
> +}
> +
> +/*----------------------------------------------------------------------------
>  | Normalizes the subnormal single-precision floating-point value represented
>  | by the denormalized significand `aSig'.  The normalized exponent and
>  | significand are stored at the locations pointed to by `zExpPtr' and
> @@ -368,6 +381,21 @@ INLINE flag extractFloat64Sign( float64 a )
>  }
>  
>  /*----------------------------------------------------------------------------
> +| If `a' is denormal and we are in flush-to-zero mode then set the
> +| input-denormal exception and return zero. Otherwise just return the value.
> +*----------------------------------------------------------------------------*/
> +static float64 float64_squash_input_denormal(float64 a STATUS_PARAM)
> +{
> +    if (STATUS(flush_inputs_to_zero)) {
> +        if (extractFloat64Exp(a) == 0 && extractFloat64Frac(a) != 0) {
> +            float_raise(float_flag_input_denormal STATUS_VAR);
> +            return make_float64(float64_val(a) & (1ULL << 63));
> +        }
> +    }
> +    return a;
> +}
> +
> +/*----------------------------------------------------------------------------
>  | Normalizes the subnormal double-precision floating-point value represented
>  | by the denormalized significand `aSig'.  The normalized exponent and
>  | significand are stored at the locations pointed to by `zExpPtr' and
> @@ -1298,6 +1326,7 @@ int32 float32_to_int32( float32 a STATUS_PARAM )
>      bits32 aSig;
>      bits64 aSig64;
>  
> +    a = float32_squash_input_denormal(a STATUS_VAR);
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
>      aSign = extractFloat32Sign( a );
> @@ -1327,6 +1356,7 @@ int32 float32_to_int32_round_to_zero( float32 a STATUS_PARAM )
>      int16 aExp, shiftCount;
>      bits32 aSig;
>      int32 z;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
>  
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
> @@ -1418,6 +1448,7 @@ int64 float32_to_int64( float32 a STATUS_PARAM )
>      int16 aExp, shiftCount;
>      bits32 aSig;
>      bits64 aSig64, aSigExtra;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
>  
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
> @@ -1455,6 +1486,7 @@ int64 float32_to_int64_round_to_zero( float32 a STATUS_PARAM )
>      bits32 aSig;
>      bits64 aSig64;
>      int64 z;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
>  
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
> @@ -1496,6 +1528,7 @@ float64 float32_to_float64( float32 a STATUS_PARAM )
>      flag aSign;
>      int16 aExp;
>      bits32 aSig;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
>  
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
> @@ -1528,6 +1561,7 @@ floatx80 float32_to_floatx80( float32 a STATUS_PARAM )
>      int16 aExp;
>      bits32 aSig;
>  
> +    a = float32_squash_input_denormal(a STATUS_VAR);
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
>      aSign = extractFloat32Sign( a );
> @@ -1561,6 +1595,7 @@ float128 float32_to_float128( float32 a STATUS_PARAM )
>      int16 aExp;
>      bits32 aSig;
>  
> +    a = float32_squash_input_denormal(a STATUS_VAR);
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
>      aSign = extractFloat32Sign( a );
> @@ -1593,6 +1628,7 @@ float32 float32_round_to_int( float32 a STATUS_PARAM)
>      bits32 lastBitMask, roundBitsMask;
>      int8 roundingMode;
>      bits32 z;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
>  
>      aExp = extractFloat32Exp( a );
>      if ( 0x96 <= aExp ) {
> @@ -1796,6 +1832,8 @@ static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM)
>  float32 float32_add( float32 a, float32 b STATUS_PARAM )
>  {
>      flag aSign, bSign;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
> +    b = float32_squash_input_denormal(b STATUS_VAR);
>  
>      aSign = extractFloat32Sign( a );
>      bSign = extractFloat32Sign( b );
> @@ -1817,6 +1855,8 @@ float32 float32_add( float32 a, float32 b STATUS_PARAM )
>  float32 float32_sub( float32 a, float32 b STATUS_PARAM )
>  {
>      flag aSign, bSign;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
> +    b = float32_squash_input_denormal(b STATUS_VAR);
>  
>      aSign = extractFloat32Sign( a );
>      bSign = extractFloat32Sign( b );
> @@ -1843,6 +1883,9 @@ float32 float32_mul( float32 a, float32 b STATUS_PARAM )
>      bits64 zSig64;
>      bits32 zSig;
>  
> +    a = float32_squash_input_denormal(a STATUS_VAR);
> +    b = float32_squash_input_denormal(b STATUS_VAR);
> +
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
>      aSign = extractFloat32Sign( a );
> @@ -1900,6 +1943,8 @@ float32 float32_div( float32 a, float32 b STATUS_PARAM )
>      flag aSign, bSign, zSign;
>      int16 aExp, bExp, zExp;
>      bits32 aSig, bSig, zSig;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
> +    b = float32_squash_input_denormal(b STATUS_VAR);
>  
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
> @@ -1966,6 +2011,8 @@ float32 float32_rem( float32 a, float32 b STATUS_PARAM )
>      bits64 aSig64, bSig64, q64;
>      bits32 alternateASig;
>      sbits32 sigMean;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
> +    b = float32_squash_input_denormal(b STATUS_VAR);
>  
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
> @@ -2062,6 +2109,7 @@ float32 float32_sqrt( float32 a STATUS_PARAM )
>      int16 aExp, zExp;
>      bits32 aSig, zSig;
>      bits64 rem, term;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
>  
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
> @@ -2148,6 +2196,7 @@ float32 float32_exp2( float32 a STATUS_PARAM )
>      bits32 aSig;
>      float64 r, x, xn;
>      int i;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
>  
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
> @@ -2194,6 +2243,7 @@ float32 float32_log2( float32 a STATUS_PARAM )
>      int16 aExp;
>      bits32 aSig, zSig, i;
>  
> +    a = float32_squash_input_denormal(a STATUS_VAR);
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
>      aSign = extractFloat32Sign( a );
> @@ -2238,6 +2288,8 @@ float32 float32_log2( float32 a STATUS_PARAM )
>  
>  int float32_eq( float32 a, float32 b STATUS_PARAM )
>  {
> +    a = float32_squash_input_denormal(a STATUS_VAR);
> +    b = float32_squash_input_denormal(b STATUS_VAR);
>  
>      if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
>           || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
> @@ -2263,6 +2315,8 @@ int float32_le( float32 a, float32 b STATUS_PARAM )
>  {
>      flag aSign, bSign;
>      bits32 av, bv;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
> +    b = float32_squash_input_denormal(b STATUS_VAR);
>  
>      if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
>           || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
> @@ -2289,6 +2343,8 @@ int float32_lt( float32 a, float32 b STATUS_PARAM )
>  {
>      flag aSign, bSign;
>      bits32 av, bv;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
> +    b = float32_squash_input_denormal(b STATUS_VAR);
>  
>      if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
>           || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
> @@ -2315,6 +2371,8 @@ int float32_lt( float32 a, float32 b STATUS_PARAM )
>  int float32_eq_signaling( float32 a, float32 b STATUS_PARAM )
>  {
>      bits32 av, bv;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
> +    b = float32_squash_input_denormal(b STATUS_VAR);
>  
>      if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
>           || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
> @@ -2339,6 +2397,8 @@ int float32_le_quiet( float32 a, float32 b STATUS_PARAM )
>  {
>      flag aSign, bSign;
>      bits32 av, bv;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
> +    b = float32_squash_input_denormal(b STATUS_VAR);
>  
>      if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
>           || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
> @@ -2368,6 +2428,8 @@ int float32_lt_quiet( float32 a, float32 b STATUS_PARAM )
>  {
>      flag aSign, bSign;
>      bits32 av, bv;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
> +    b = float32_squash_input_denormal(b STATUS_VAR);
>  
>      if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
>           || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
> @@ -2401,6 +2463,7 @@ int32 float64_to_int32( float64 a STATUS_PARAM )
>      flag aSign;
>      int16 aExp, shiftCount;
>      bits64 aSig;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
>  
>      aSig = extractFloat64Frac( a );
>      aExp = extractFloat64Exp( a );
> @@ -2429,6 +2492,7 @@ int32 float64_to_int32_round_to_zero( float64 a STATUS_PARAM )
>      int16 aExp, shiftCount;
>      bits64 aSig, savedASig;
>      int32 z;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
>  
>      aSig = extractFloat64Frac( a );
>      aExp = extractFloat64Exp( a );
> @@ -2525,6 +2589,7 @@ int64 float64_to_int64( float64 a STATUS_PARAM )
>      flag aSign;
>      int16 aExp, shiftCount;
>      bits64 aSig, aSigExtra;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
>  
>      aSig = extractFloat64Frac( a );
>      aExp = extractFloat64Exp( a );
> @@ -2568,6 +2633,7 @@ int64 float64_to_int64_round_to_zero( float64 a STATUS_PARAM )
>      int16 aExp, shiftCount;
>      bits64 aSig;
>      int64 z;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
>  
>      aSig = extractFloat64Frac( a );
>      aExp = extractFloat64Exp( a );
> @@ -2617,6 +2683,7 @@ float32 float64_to_float32( float64 a STATUS_PARAM )
>      int16 aExp;
>      bits64 aSig;
>      bits32 zSig;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
>  
>      aSig = extractFloat64Frac( a );
>      aExp = extractFloat64Exp( a );
> @@ -2694,6 +2761,7 @@ bits16 float32_to_float16( float32 a, flag ieee STATUS_PARAM)
>      bits32 mask;
>      bits32 increment;
>      int8 roundingMode;
> +    a = float32_squash_input_denormal(a STATUS_VAR);
>  
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
> @@ -2788,6 +2856,7 @@ floatx80 float64_to_floatx80( float64 a STATUS_PARAM )
>      int16 aExp;
>      bits64 aSig;
>  
> +    a = float64_squash_input_denormal(a STATUS_VAR);
>      aSig = extractFloat64Frac( a );
>      aExp = extractFloat64Exp( a );
>      aSign = extractFloat64Sign( a );
> @@ -2822,6 +2891,7 @@ float128 float64_to_float128( float64 a STATUS_PARAM )
>      int16 aExp;
>      bits64 aSig, zSig0, zSig1;
>  
> +    a = float64_squash_input_denormal(a STATUS_VAR);
>      aSig = extractFloat64Frac( a );
>      aExp = extractFloat64Exp( a );
>      aSign = extractFloat64Sign( a );
> @@ -2855,6 +2925,7 @@ float64 float64_round_to_int( float64 a STATUS_PARAM )
>      bits64 lastBitMask, roundBitsMask;
>      int8 roundingMode;
>      bits64 z;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
>  
>      aExp = extractFloat64Exp( a );
>      if ( 0x433 <= aExp ) {
> @@ -3071,6 +3142,8 @@ static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM )
>  float64 float64_add( float64 a, float64 b STATUS_PARAM )
>  {
>      flag aSign, bSign;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
> +    b = float64_squash_input_denormal(b STATUS_VAR);
>  
>      aSign = extractFloat64Sign( a );
>      bSign = extractFloat64Sign( b );
> @@ -3092,6 +3165,8 @@ float64 float64_add( float64 a, float64 b STATUS_PARAM )
>  float64 float64_sub( float64 a, float64 b STATUS_PARAM )
>  {
>      flag aSign, bSign;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
> +    b = float64_squash_input_denormal(b STATUS_VAR);
>  
>      aSign = extractFloat64Sign( a );
>      bSign = extractFloat64Sign( b );
> @@ -3116,6 +3191,9 @@ float64 float64_mul( float64 a, float64 b STATUS_PARAM )
>      int16 aExp, bExp, zExp;
>      bits64 aSig, bSig, zSig0, zSig1;
>  
> +    a = float64_squash_input_denormal(a STATUS_VAR);
> +    b = float64_squash_input_denormal(b STATUS_VAR);
> +
>      aSig = extractFloat64Frac( a );
>      aExp = extractFloat64Exp( a );
>      aSign = extractFloat64Sign( a );
> @@ -3175,6 +3253,8 @@ float64 float64_div( float64 a, float64 b STATUS_PARAM )
>      bits64 aSig, bSig, zSig;
>      bits64 rem0, rem1;
>      bits64 term0, term1;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
> +    b = float64_squash_input_denormal(b STATUS_VAR);
>  
>      aSig = extractFloat64Frac( a );
>      aExp = extractFloat64Exp( a );
> @@ -3246,6 +3326,8 @@ float64 float64_rem( float64 a, float64 b STATUS_PARAM )
>      bits64 q, alternateASig;
>      sbits64 sigMean;
>  
> +    a = float64_squash_input_denormal(a STATUS_VAR);
> +    b = float64_squash_input_denormal(b STATUS_VAR);
>      aSig = extractFloat64Frac( a );
>      aExp = extractFloat64Exp( a );
>      aSign = extractFloat64Sign( a );
> @@ -3328,6 +3410,7 @@ float64 float64_sqrt( float64 a STATUS_PARAM )
>      int16 aExp, zExp;
>      bits64 aSig, zSig, doubleZSig;
>      bits64 rem0, rem1, term0, term1;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
>  
>      aSig = extractFloat64Frac( a );
>      aExp = extractFloat64Exp( a );
> @@ -3377,6 +3460,7 @@ float64 float64_log2( float64 a STATUS_PARAM )
>      flag aSign, zSign;
>      int16 aExp;
>      bits64 aSig, aSig0, aSig1, zSig, i;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
>  
>      aSig = extractFloat64Frac( a );
>      aExp = extractFloat64Exp( a );
> @@ -3422,6 +3506,8 @@ float64 float64_log2( float64 a STATUS_PARAM )
>  int float64_eq( float64 a, float64 b STATUS_PARAM )
>  {
>      bits64 av, bv;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
> +    b = float64_squash_input_denormal(b STATUS_VAR);
>  
>      if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
>           || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
> @@ -3448,6 +3534,8 @@ int float64_le( float64 a, float64 b STATUS_PARAM )
>  {
>      flag aSign, bSign;
>      bits64 av, bv;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
> +    b = float64_squash_input_denormal(b STATUS_VAR);
>  
>      if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
>           || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
> @@ -3475,6 +3563,8 @@ int float64_lt( float64 a, float64 b STATUS_PARAM )
>      flag aSign, bSign;
>      bits64 av, bv;
>  
> +    a = float64_squash_input_denormal(a STATUS_VAR);
> +    b = float64_squash_input_denormal(b STATUS_VAR);
>      if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
>           || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
>         ) {
> @@ -3500,6 +3590,8 @@ int float64_lt( float64 a, float64 b STATUS_PARAM )
>  int float64_eq_signaling( float64 a, float64 b STATUS_PARAM )
>  {
>      bits64 av, bv;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
> +    b = float64_squash_input_denormal(b STATUS_VAR);
>  
>      if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
>           || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
> @@ -3524,6 +3616,8 @@ int float64_le_quiet( float64 a, float64 b STATUS_PARAM )
>  {
>      flag aSign, bSign;
>      bits64 av, bv;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
> +    b = float64_squash_input_denormal(b STATUS_VAR);
>  
>      if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
>           || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
> @@ -3553,6 +3647,8 @@ int float64_lt_quiet( float64 a, float64 b STATUS_PARAM )
>  {
>      flag aSign, bSign;
>      bits64 av, bv;
> +    a = float64_squash_input_denormal(a STATUS_VAR);
> +    b = float64_squash_input_denormal(b STATUS_VAR);
>  
>      if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
>           || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
> @@ -5833,6 +5929,8 @@ INLINE int float ## s ## _compare_internal( float ## s a, float ## s b,      \
>  {                                                                            \
>      flag aSign, bSign;                                                       \
>      bits ## s av, bv;                                                        \
> +    a = float ## s ## _squash_input_denormal(a STATUS_VAR);                  \
> +    b = float ## s ## _squash_input_denormal(b STATUS_VAR);                  \
>                                                                               \
>      if (( ( extractFloat ## s ## Exp( a ) == nan_exp ) &&                    \
>           extractFloat ## s ## Frac( a ) ) ||                                 \
> @@ -5929,6 +6027,7 @@ float32 float32_scalbn( float32 a, int n STATUS_PARAM )
>      int16 aExp;
>      bits32 aSig;
>  
> +    a = float32_squash_input_denormal(a STATUS_VAR);
>      aSig = extractFloat32Frac( a );
>      aExp = extractFloat32Exp( a );
>      aSign = extractFloat32Sign( a );
> @@ -5952,6 +6051,7 @@ float64 float64_scalbn( float64 a, int n STATUS_PARAM )
>      int16 aExp;
>      bits64 aSig;
>  
> +    a = float64_squash_input_denormal(a STATUS_VAR);
>      aSig = extractFloat64Frac( a );
>      aExp = extractFloat64Exp( a );
>      aSign = extractFloat64Sign( a );
> diff --git a/fpu/softfloat.h b/fpu/softfloat.h
> index 1f37877..9a72ee5 100644
> --- a/fpu/softfloat.h
> +++ b/fpu/softfloat.h
> @@ -180,7 +180,8 @@ enum {
>      float_flag_divbyzero =  4,
>      float_flag_overflow  =  8,
>      float_flag_underflow = 16,
> -    float_flag_inexact   = 32
> +    float_flag_inexact   = 32,
> +    float_flag_input_denormal = 64
>  };
>  
>  typedef struct float_status {
> @@ -190,7 +191,10 @@ typedef struct float_status {
>  #ifdef FLOATX80
>      signed char floatx80_rounding_precision;
>  #endif
> +    /* should denormalised results go to zero and set the inexact flag? */
>      flag flush_to_zero;
> +    /* should denormalised inputs go to zero and set the input_denormal flag? */
> +    flag flush_inputs_to_zero;
>      flag default_nan_mode;
>  } float_status;
>  
> @@ -200,6 +204,10 @@ INLINE void set_flush_to_zero(flag val STATUS_PARAM)
>  {
>      STATUS(flush_to_zero) = val;
>  }
> +INLINE void set_flush_inputs_to_zero(flag val STATUS_PARAM)
> +{
> +    STATUS(flush_inputs_to_zero) = val;
> +}
>  INLINE void set_default_nan_mode(flag val STATUS_PARAM)
>  {
>      STATUS(default_nan_mode) = val;
> @@ -294,11 +302,17 @@ float32 float32_scalbn( float32, int STATUS_PARAM );
>  
>  INLINE float32 float32_abs(float32 a)
>  {
> +    /* Note that abs does *not* handle NaN specially, nor does
> +     * it flush denormal inputs to zero.
> +     */
>      return make_float32(float32_val(a) & 0x7fffffff);
>  }
>  
>  INLINE float32 float32_chs(float32 a)
>  {
> +    /* Note that chs does *not* handle NaN specially, nor does
> +     * it flush denormal inputs to zero.
> +     */
>      return make_float32(float32_val(a) ^ 0x80000000);
>  }
>  
> @@ -374,11 +388,17 @@ float64 float64_scalbn( float64, int STATUS_PARAM );
>  
>  INLINE float64 float64_abs(float64 a)
>  {
> +    /* Note that abs does *not* handle NaN specially, nor does
> +     * it flush denormal inputs to zero.
> +     */
>      return make_float64(float64_val(a) & 0x7fffffffffffffffLL);
>  }
>  
>  INLINE float64 float64_chs(float64 a)
>  {
> +    /* Note that chs does *not* handle NaN specially, nor does
> +     * it flush denormal inputs to zero.
> +     */
>      return make_float64(float64_val(a) ^ 0x8000000000000000LL);
>  }
>  
> -- 
> 1.6.3.3
> 
> 
>
Andreas Färber - Jan. 4, 2011, 9:13 p.m.
Am 04.01.2011 um 16:55 schrieb Aurelien Jarno:

> On Mon, Jan 03, 2011 at 04:48:53PM +0000, Peter Maydell wrote:
>> Add support to softfloat for flushing input denormal float32 and  
>> float64
>> to zero. softfloat's existing 'flush_to_zero' flag only flushes  
>> denormals
>> to zero on output. Some CPUs need input denormals to be flushed  
>> before
>> processing as well. Implement this, using a new status flag to  
>> enable it
>> and a new exception status bit to indicate when it has happened.  
>> Existing
>> CPUs should be unaffected as there is no behaviour change unless the
>> mode is enabled.
>>
>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>
> Acked-by: Aurelien Jarno <aurelien@aurel32.net>

No conflicts spotted. If it's semantically correct, fine with me.

Andreas

Patch

diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 6f5b05d..17842f4 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -30,8 +30,6 @@  these four paragraphs for those parts of this code that are retained.
 
 =============================================================================*/
 
-/* FIXME: Flush-To-Zero only effects results.  Denormal inputs should also
-   be flushed to zero.  */
 #include "softfloat.h"
 
 /*----------------------------------------------------------------------------
@@ -204,6 +202,21 @@  INLINE flag extractFloat32Sign( float32 a )
 }
 
 /*----------------------------------------------------------------------------
+| If `a' is denormal and we are in flush-to-zero mode then set the
+| input-denormal exception and return zero. Otherwise just return the value.
+*----------------------------------------------------------------------------*/
+static float32 float32_squash_input_denormal(float32 a STATUS_PARAM)
+{
+    if (STATUS(flush_inputs_to_zero)) {
+        if (extractFloat32Exp(a) == 0 && extractFloat32Frac(a) != 0) {
+            float_raise(float_flag_input_denormal STATUS_VAR);
+            return make_float32(float32_val(a) & 0x80000000);
+        }
+    }
+    return a;
+}
+
+/*----------------------------------------------------------------------------
 | Normalizes the subnormal single-precision floating-point value represented
 | by the denormalized significand `aSig'.  The normalized exponent and
 | significand are stored at the locations pointed to by `zExpPtr' and
@@ -368,6 +381,21 @@  INLINE flag extractFloat64Sign( float64 a )
 }
 
 /*----------------------------------------------------------------------------
+| If `a' is denormal and we are in flush-to-zero mode then set the
+| input-denormal exception and return zero. Otherwise just return the value.
+*----------------------------------------------------------------------------*/
+static float64 float64_squash_input_denormal(float64 a STATUS_PARAM)
+{
+    if (STATUS(flush_inputs_to_zero)) {
+        if (extractFloat64Exp(a) == 0 && extractFloat64Frac(a) != 0) {
+            float_raise(float_flag_input_denormal STATUS_VAR);
+            return make_float64(float64_val(a) & (1ULL << 63));
+        }
+    }
+    return a;
+}
+
+/*----------------------------------------------------------------------------
 | Normalizes the subnormal double-precision floating-point value represented
 | by the denormalized significand `aSig'.  The normalized exponent and
 | significand are stored at the locations pointed to by `zExpPtr' and
@@ -1298,6 +1326,7 @@  int32 float32_to_int32( float32 a STATUS_PARAM )
     bits32 aSig;
     bits64 aSig64;
 
+    a = float32_squash_input_denormal(a STATUS_VAR);
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
     aSign = extractFloat32Sign( a );
@@ -1327,6 +1356,7 @@  int32 float32_to_int32_round_to_zero( float32 a STATUS_PARAM )
     int16 aExp, shiftCount;
     bits32 aSig;
     int32 z;
+    a = float32_squash_input_denormal(a STATUS_VAR);
 
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
@@ -1418,6 +1448,7 @@  int64 float32_to_int64( float32 a STATUS_PARAM )
     int16 aExp, shiftCount;
     bits32 aSig;
     bits64 aSig64, aSigExtra;
+    a = float32_squash_input_denormal(a STATUS_VAR);
 
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
@@ -1455,6 +1486,7 @@  int64 float32_to_int64_round_to_zero( float32 a STATUS_PARAM )
     bits32 aSig;
     bits64 aSig64;
     int64 z;
+    a = float32_squash_input_denormal(a STATUS_VAR);
 
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
@@ -1496,6 +1528,7 @@  float64 float32_to_float64( float32 a STATUS_PARAM )
     flag aSign;
     int16 aExp;
     bits32 aSig;
+    a = float32_squash_input_denormal(a STATUS_VAR);
 
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
@@ -1528,6 +1561,7 @@  floatx80 float32_to_floatx80( float32 a STATUS_PARAM )
     int16 aExp;
     bits32 aSig;
 
+    a = float32_squash_input_denormal(a STATUS_VAR);
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
     aSign = extractFloat32Sign( a );
@@ -1561,6 +1595,7 @@  float128 float32_to_float128( float32 a STATUS_PARAM )
     int16 aExp;
     bits32 aSig;
 
+    a = float32_squash_input_denormal(a STATUS_VAR);
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
     aSign = extractFloat32Sign( a );
@@ -1593,6 +1628,7 @@  float32 float32_round_to_int( float32 a STATUS_PARAM)
     bits32 lastBitMask, roundBitsMask;
     int8 roundingMode;
     bits32 z;
+    a = float32_squash_input_denormal(a STATUS_VAR);
 
     aExp = extractFloat32Exp( a );
     if ( 0x96 <= aExp ) {
@@ -1796,6 +1832,8 @@  static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM)
 float32 float32_add( float32 a, float32 b STATUS_PARAM )
 {
     flag aSign, bSign;
+    a = float32_squash_input_denormal(a STATUS_VAR);
+    b = float32_squash_input_denormal(b STATUS_VAR);
 
     aSign = extractFloat32Sign( a );
     bSign = extractFloat32Sign( b );
@@ -1817,6 +1855,8 @@  float32 float32_add( float32 a, float32 b STATUS_PARAM )
 float32 float32_sub( float32 a, float32 b STATUS_PARAM )
 {
     flag aSign, bSign;
+    a = float32_squash_input_denormal(a STATUS_VAR);
+    b = float32_squash_input_denormal(b STATUS_VAR);
 
     aSign = extractFloat32Sign( a );
     bSign = extractFloat32Sign( b );
@@ -1843,6 +1883,9 @@  float32 float32_mul( float32 a, float32 b STATUS_PARAM )
     bits64 zSig64;
     bits32 zSig;
 
+    a = float32_squash_input_denormal(a STATUS_VAR);
+    b = float32_squash_input_denormal(b STATUS_VAR);
+
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
     aSign = extractFloat32Sign( a );
@@ -1900,6 +1943,8 @@  float32 float32_div( float32 a, float32 b STATUS_PARAM )
     flag aSign, bSign, zSign;
     int16 aExp, bExp, zExp;
     bits32 aSig, bSig, zSig;
+    a = float32_squash_input_denormal(a STATUS_VAR);
+    b = float32_squash_input_denormal(b STATUS_VAR);
 
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
@@ -1966,6 +2011,8 @@  float32 float32_rem( float32 a, float32 b STATUS_PARAM )
     bits64 aSig64, bSig64, q64;
     bits32 alternateASig;
     sbits32 sigMean;
+    a = float32_squash_input_denormal(a STATUS_VAR);
+    b = float32_squash_input_denormal(b STATUS_VAR);
 
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
@@ -2062,6 +2109,7 @@  float32 float32_sqrt( float32 a STATUS_PARAM )
     int16 aExp, zExp;
     bits32 aSig, zSig;
     bits64 rem, term;
+    a = float32_squash_input_denormal(a STATUS_VAR);
 
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
@@ -2148,6 +2196,7 @@  float32 float32_exp2( float32 a STATUS_PARAM )
     bits32 aSig;
     float64 r, x, xn;
     int i;
+    a = float32_squash_input_denormal(a STATUS_VAR);
 
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
@@ -2194,6 +2243,7 @@  float32 float32_log2( float32 a STATUS_PARAM )
     int16 aExp;
     bits32 aSig, zSig, i;
 
+    a = float32_squash_input_denormal(a STATUS_VAR);
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
     aSign = extractFloat32Sign( a );
@@ -2238,6 +2288,8 @@  float32 float32_log2( float32 a STATUS_PARAM )
 
 int float32_eq( float32 a, float32 b STATUS_PARAM )
 {
+    a = float32_squash_input_denormal(a STATUS_VAR);
+    b = float32_squash_input_denormal(b STATUS_VAR);
 
     if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
          || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
@@ -2263,6 +2315,8 @@  int float32_le( float32 a, float32 b STATUS_PARAM )
 {
     flag aSign, bSign;
     bits32 av, bv;
+    a = float32_squash_input_denormal(a STATUS_VAR);
+    b = float32_squash_input_denormal(b STATUS_VAR);
 
     if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
          || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
@@ -2289,6 +2343,8 @@  int float32_lt( float32 a, float32 b STATUS_PARAM )
 {
     flag aSign, bSign;
     bits32 av, bv;
+    a = float32_squash_input_denormal(a STATUS_VAR);
+    b = float32_squash_input_denormal(b STATUS_VAR);
 
     if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
          || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
@@ -2315,6 +2371,8 @@  int float32_lt( float32 a, float32 b STATUS_PARAM )
 int float32_eq_signaling( float32 a, float32 b STATUS_PARAM )
 {
     bits32 av, bv;
+    a = float32_squash_input_denormal(a STATUS_VAR);
+    b = float32_squash_input_denormal(b STATUS_VAR);
 
     if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
          || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
@@ -2339,6 +2397,8 @@  int float32_le_quiet( float32 a, float32 b STATUS_PARAM )
 {
     flag aSign, bSign;
     bits32 av, bv;
+    a = float32_squash_input_denormal(a STATUS_VAR);
+    b = float32_squash_input_denormal(b STATUS_VAR);
 
     if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
          || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
@@ -2368,6 +2428,8 @@  int float32_lt_quiet( float32 a, float32 b STATUS_PARAM )
 {
     flag aSign, bSign;
     bits32 av, bv;
+    a = float32_squash_input_denormal(a STATUS_VAR);
+    b = float32_squash_input_denormal(b STATUS_VAR);
 
     if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
          || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
@@ -2401,6 +2463,7 @@  int32 float64_to_int32( float64 a STATUS_PARAM )
     flag aSign;
     int16 aExp, shiftCount;
     bits64 aSig;
+    a = float64_squash_input_denormal(a STATUS_VAR);
 
     aSig = extractFloat64Frac( a );
     aExp = extractFloat64Exp( a );
@@ -2429,6 +2492,7 @@  int32 float64_to_int32_round_to_zero( float64 a STATUS_PARAM )
     int16 aExp, shiftCount;
     bits64 aSig, savedASig;
     int32 z;
+    a = float64_squash_input_denormal(a STATUS_VAR);
 
     aSig = extractFloat64Frac( a );
     aExp = extractFloat64Exp( a );
@@ -2525,6 +2589,7 @@  int64 float64_to_int64( float64 a STATUS_PARAM )
     flag aSign;
     int16 aExp, shiftCount;
     bits64 aSig, aSigExtra;
+    a = float64_squash_input_denormal(a STATUS_VAR);
 
     aSig = extractFloat64Frac( a );
     aExp = extractFloat64Exp( a );
@@ -2568,6 +2633,7 @@  int64 float64_to_int64_round_to_zero( float64 a STATUS_PARAM )
     int16 aExp, shiftCount;
     bits64 aSig;
     int64 z;
+    a = float64_squash_input_denormal(a STATUS_VAR);
 
     aSig = extractFloat64Frac( a );
     aExp = extractFloat64Exp( a );
@@ -2617,6 +2683,7 @@  float32 float64_to_float32( float64 a STATUS_PARAM )
     int16 aExp;
     bits64 aSig;
     bits32 zSig;
+    a = float64_squash_input_denormal(a STATUS_VAR);
 
     aSig = extractFloat64Frac( a );
     aExp = extractFloat64Exp( a );
@@ -2694,6 +2761,7 @@  bits16 float32_to_float16( float32 a, flag ieee STATUS_PARAM)
     bits32 mask;
     bits32 increment;
     int8 roundingMode;
+    a = float32_squash_input_denormal(a STATUS_VAR);
 
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
@@ -2788,6 +2856,7 @@  floatx80 float64_to_floatx80( float64 a STATUS_PARAM )
     int16 aExp;
     bits64 aSig;
 
+    a = float64_squash_input_denormal(a STATUS_VAR);
     aSig = extractFloat64Frac( a );
     aExp = extractFloat64Exp( a );
     aSign = extractFloat64Sign( a );
@@ -2822,6 +2891,7 @@  float128 float64_to_float128( float64 a STATUS_PARAM )
     int16 aExp;
     bits64 aSig, zSig0, zSig1;
 
+    a = float64_squash_input_denormal(a STATUS_VAR);
     aSig = extractFloat64Frac( a );
     aExp = extractFloat64Exp( a );
     aSign = extractFloat64Sign( a );
@@ -2855,6 +2925,7 @@  float64 float64_round_to_int( float64 a STATUS_PARAM )
     bits64 lastBitMask, roundBitsMask;
     int8 roundingMode;
     bits64 z;
+    a = float64_squash_input_denormal(a STATUS_VAR);
 
     aExp = extractFloat64Exp( a );
     if ( 0x433 <= aExp ) {
@@ -3071,6 +3142,8 @@  static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM )
 float64 float64_add( float64 a, float64 b STATUS_PARAM )
 {
     flag aSign, bSign;
+    a = float64_squash_input_denormal(a STATUS_VAR);
+    b = float64_squash_input_denormal(b STATUS_VAR);
 
     aSign = extractFloat64Sign( a );
     bSign = extractFloat64Sign( b );
@@ -3092,6 +3165,8 @@  float64 float64_add( float64 a, float64 b STATUS_PARAM )
 float64 float64_sub( float64 a, float64 b STATUS_PARAM )
 {
     flag aSign, bSign;
+    a = float64_squash_input_denormal(a STATUS_VAR);
+    b = float64_squash_input_denormal(b STATUS_VAR);
 
     aSign = extractFloat64Sign( a );
     bSign = extractFloat64Sign( b );
@@ -3116,6 +3191,9 @@  float64 float64_mul( float64 a, float64 b STATUS_PARAM )
     int16 aExp, bExp, zExp;
     bits64 aSig, bSig, zSig0, zSig1;
 
+    a = float64_squash_input_denormal(a STATUS_VAR);
+    b = float64_squash_input_denormal(b STATUS_VAR);
+
     aSig = extractFloat64Frac( a );
     aExp = extractFloat64Exp( a );
     aSign = extractFloat64Sign( a );
@@ -3175,6 +3253,8 @@  float64 float64_div( float64 a, float64 b STATUS_PARAM )
     bits64 aSig, bSig, zSig;
     bits64 rem0, rem1;
     bits64 term0, term1;
+    a = float64_squash_input_denormal(a STATUS_VAR);
+    b = float64_squash_input_denormal(b STATUS_VAR);
 
     aSig = extractFloat64Frac( a );
     aExp = extractFloat64Exp( a );
@@ -3246,6 +3326,8 @@  float64 float64_rem( float64 a, float64 b STATUS_PARAM )
     bits64 q, alternateASig;
     sbits64 sigMean;
 
+    a = float64_squash_input_denormal(a STATUS_VAR);
+    b = float64_squash_input_denormal(b STATUS_VAR);
     aSig = extractFloat64Frac( a );
     aExp = extractFloat64Exp( a );
     aSign = extractFloat64Sign( a );
@@ -3328,6 +3410,7 @@  float64 float64_sqrt( float64 a STATUS_PARAM )
     int16 aExp, zExp;
     bits64 aSig, zSig, doubleZSig;
     bits64 rem0, rem1, term0, term1;
+    a = float64_squash_input_denormal(a STATUS_VAR);
 
     aSig = extractFloat64Frac( a );
     aExp = extractFloat64Exp( a );
@@ -3377,6 +3460,7 @@  float64 float64_log2( float64 a STATUS_PARAM )
     flag aSign, zSign;
     int16 aExp;
     bits64 aSig, aSig0, aSig1, zSig, i;
+    a = float64_squash_input_denormal(a STATUS_VAR);
 
     aSig = extractFloat64Frac( a );
     aExp = extractFloat64Exp( a );
@@ -3422,6 +3506,8 @@  float64 float64_log2( float64 a STATUS_PARAM )
 int float64_eq( float64 a, float64 b STATUS_PARAM )
 {
     bits64 av, bv;
+    a = float64_squash_input_denormal(a STATUS_VAR);
+    b = float64_squash_input_denormal(b STATUS_VAR);
 
     if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
          || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
@@ -3448,6 +3534,8 @@  int float64_le( float64 a, float64 b STATUS_PARAM )
 {
     flag aSign, bSign;
     bits64 av, bv;
+    a = float64_squash_input_denormal(a STATUS_VAR);
+    b = float64_squash_input_denormal(b STATUS_VAR);
 
     if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
          || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
@@ -3475,6 +3563,8 @@  int float64_lt( float64 a, float64 b STATUS_PARAM )
     flag aSign, bSign;
     bits64 av, bv;
 
+    a = float64_squash_input_denormal(a STATUS_VAR);
+    b = float64_squash_input_denormal(b STATUS_VAR);
     if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
          || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
        ) {
@@ -3500,6 +3590,8 @@  int float64_lt( float64 a, float64 b STATUS_PARAM )
 int float64_eq_signaling( float64 a, float64 b STATUS_PARAM )
 {
     bits64 av, bv;
+    a = float64_squash_input_denormal(a STATUS_VAR);
+    b = float64_squash_input_denormal(b STATUS_VAR);
 
     if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
          || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
@@ -3524,6 +3616,8 @@  int float64_le_quiet( float64 a, float64 b STATUS_PARAM )
 {
     flag aSign, bSign;
     bits64 av, bv;
+    a = float64_squash_input_denormal(a STATUS_VAR);
+    b = float64_squash_input_denormal(b STATUS_VAR);
 
     if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
          || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
@@ -3553,6 +3647,8 @@  int float64_lt_quiet( float64 a, float64 b STATUS_PARAM )
 {
     flag aSign, bSign;
     bits64 av, bv;
+    a = float64_squash_input_denormal(a STATUS_VAR);
+    b = float64_squash_input_denormal(b STATUS_VAR);
 
     if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
          || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
@@ -5833,6 +5929,8 @@  INLINE int float ## s ## _compare_internal( float ## s a, float ## s b,      \
 {                                                                            \
     flag aSign, bSign;                                                       \
     bits ## s av, bv;                                                        \
+    a = float ## s ## _squash_input_denormal(a STATUS_VAR);                  \
+    b = float ## s ## _squash_input_denormal(b STATUS_VAR);                  \
                                                                              \
     if (( ( extractFloat ## s ## Exp( a ) == nan_exp ) &&                    \
          extractFloat ## s ## Frac( a ) ) ||                                 \
@@ -5929,6 +6027,7 @@  float32 float32_scalbn( float32 a, int n STATUS_PARAM )
     int16 aExp;
     bits32 aSig;
 
+    a = float32_squash_input_denormal(a STATUS_VAR);
     aSig = extractFloat32Frac( a );
     aExp = extractFloat32Exp( a );
     aSign = extractFloat32Sign( a );
@@ -5952,6 +6051,7 @@  float64 float64_scalbn( float64 a, int n STATUS_PARAM )
     int16 aExp;
     bits64 aSig;
 
+    a = float64_squash_input_denormal(a STATUS_VAR);
     aSig = extractFloat64Frac( a );
     aExp = extractFloat64Exp( a );
     aSign = extractFloat64Sign( a );
diff --git a/fpu/softfloat.h b/fpu/softfloat.h
index 1f37877..9a72ee5 100644
--- a/fpu/softfloat.h
+++ b/fpu/softfloat.h
@@ -180,7 +180,8 @@  enum {
     float_flag_divbyzero =  4,
     float_flag_overflow  =  8,
     float_flag_underflow = 16,
-    float_flag_inexact   = 32
+    float_flag_inexact   = 32,
+    float_flag_input_denormal = 64
 };
 
 typedef struct float_status {
@@ -190,7 +191,10 @@  typedef struct float_status {
 #ifdef FLOATX80
     signed char floatx80_rounding_precision;
 #endif
+    /* should denormalised results go to zero and set the inexact flag? */
     flag flush_to_zero;
+    /* should denormalised inputs go to zero and set the input_denormal flag? */
+    flag flush_inputs_to_zero;
     flag default_nan_mode;
 } float_status;
 
@@ -200,6 +204,10 @@  INLINE void set_flush_to_zero(flag val STATUS_PARAM)
 {
     STATUS(flush_to_zero) = val;
 }
+INLINE void set_flush_inputs_to_zero(flag val STATUS_PARAM)
+{
+    STATUS(flush_inputs_to_zero) = val;
+}
 INLINE void set_default_nan_mode(flag val STATUS_PARAM)
 {
     STATUS(default_nan_mode) = val;
@@ -294,11 +302,17 @@  float32 float32_scalbn( float32, int STATUS_PARAM );
 
 INLINE float32 float32_abs(float32 a)
 {
+    /* Note that abs does *not* handle NaN specially, nor does
+     * it flush denormal inputs to zero.
+     */
     return make_float32(float32_val(a) & 0x7fffffff);
 }
 
 INLINE float32 float32_chs(float32 a)
 {
+    /* Note that chs does *not* handle NaN specially, nor does
+     * it flush denormal inputs to zero.
+     */
     return make_float32(float32_val(a) ^ 0x80000000);
 }
 
@@ -374,11 +388,17 @@  float64 float64_scalbn( float64, int STATUS_PARAM );
 
 INLINE float64 float64_abs(float64 a)
 {
+    /* Note that abs does *not* handle NaN specially, nor does
+     * it flush denormal inputs to zero.
+     */
     return make_float64(float64_val(a) & 0x7fffffffffffffffLL);
 }
 
 INLINE float64 float64_chs(float64 a)
 {
+    /* Note that chs does *not* handle NaN specially, nor does
+     * it flush denormal inputs to zero.
+     */
     return make_float64(float64_val(a) ^ 0x8000000000000000LL);
 }