From patchwork Thu Dec 16 11:51:17 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 75742 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 9A6291007D3 for ; Thu, 16 Dec 2010 23:01:37 +1100 (EST) Received: from localhost ([127.0.0.1]:46994 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PTCUK-0005Z2-QW for incoming@patchwork.ozlabs.org; Thu, 16 Dec 2010 06:59:12 -0500 Received: from [140.186.70.92] (port=38728 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PTCS0-0004On-1e for qemu-devel@nongnu.org; Thu, 16 Dec 2010 06:57:07 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PTCMp-0000Lp-Lu for qemu-devel@nongnu.org; Thu, 16 Dec 2010 06:51:29 -0500 Received: from mnementh.archaic.org.uk ([81.2.115.146]:58074) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PTCMp-0000Jr-8j for qemu-devel@nongnu.org; Thu, 16 Dec 2010 06:51:27 -0500 Received: from pm215 by mnementh.archaic.org.uk with local (Exim 4.69) (envelope-from ) id 1PTCMg-0006pG-Jb; Thu, 16 Dec 2010 11:51:18 +0000 From: Peter Maydell To: qemu-devel@nongnu.org Date: Thu, 16 Dec 2010 11:51:17 +0000 Message-Id: <1292500278-26215-2-git-send-email-peter.maydell@linaro.org> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1292500278-26215-1-git-send-email-peter.maydell@linaro.org> References: <1292500278-26215-1-git-send-email-peter.maydell@linaro.org> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 2) Cc: Nathan Froyd Subject: [Qemu-devel] [PATCH 1/2] softfloat: abstract out target-specific NaN propagation rules X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org IEEE754 doesn't specify precisely what NaN should be returned as the result of an operation on two input NaNs. This is therefore target-specific. Abstract out the code in propagateFloat*NaN() which was implementing the x87 propagation rules, so that it can be easily replaced on a per-target basis. Signed-off-by: Peter Maydell --- fpu/softfloat-specialize.h | 160 +++++++++++++++++++++++++++---------------- 1 files changed, 100 insertions(+), 60 deletions(-) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 8e6aceb..3015480 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -134,6 +134,52 @@ static float32 commonNaNToFloat32( commonNaNT a ) } /*---------------------------------------------------------------------------- +| Select which NaN to propagate for a two-input operation. +| IEEE754 doesn't specify all the details of this, so the +| algorithm is target-specific. +| The routine is passed various bits of information about the +| two NaNs and should return 0 to select NaN a and 1 for NaN b. +| Note that signalling NaNs are always squashed to quiet NaNs +| by the caller, by flipping the SNaN bit before returning them. +| +| aIsLargerSignificand is only valid if both a and b are NaNs +| of some kind, and is true if a has the larger significand, +| or if both a and b have the same significand but a is +| positive but b is negative. It is only needed for the x87 +| tie-break rule. +*----------------------------------------------------------------------------*/ + +static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, + flag aIsLargerSignificand) +{ + /* This implements x87 NaN propagation rules: + * SNaN + QNaN => return the QNaN + * two SNaNs => return the one with the larger significand, silenced + * two QNaNs => return the one with the larger significand + * SNaN and a non-NaN => return the SNaN, silenced + * QNaN and a non-NaN => return the QNaN + * + * If we get down to comparing significands and they are the same, + * return the NaN with the positive sign bit (if any). + */ + if (aIsSNaN) { + if (bIsSNaN) { + return aIsLargerSignificand ? 0 : 1; + } + return bIsQNaN ? 1 : 0; + } + else if (aIsQNaN) { + if (bIsSNaN || !bIsQNaN) + return 0; + else { + return aIsLargerSignificand ? 0 : 1; + } + } else { + return 1; + } +} + +/*---------------------------------------------------------------------------- | Takes two single-precision floating-point values `a' and `b', one of which | is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a | signaling NaN, the invalid exception is raised. @@ -141,7 +187,7 @@ static float32 commonNaNToFloat32( commonNaNT a ) static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM) { - flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN, aIsLargerSignificand; bits32 av, bv, res; if ( STATUS(default_nan_mode) ) @@ -161,26 +207,22 @@ static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM) bv |= 0x00400000; #endif if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); - if ( aIsSignalingNaN ) { - if ( bIsSignalingNaN ) goto returnLargerSignificand; - res = bIsNaN ? bv : av; - } - else if ( aIsNaN ) { - if ( bIsSignalingNaN || ! bIsNaN ) - res = av; - else { - returnLargerSignificand: - if ( (bits32) ( av<<1 ) < (bits32) ( bv<<1 ) ) - res = bv; - else if ( (bits32) ( bv<<1 ) < (bits32) ( av<<1 ) ) - res = av; - else - res = ( av < bv ) ? av : bv; - } + + if ((bits32)(av<<1) < (bits32)(bv<<1)) { + aIsLargerSignificand = 0; + } else if ((bits32)(bv<<1) < (bits32)(av<<1)) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (av < bv) ? 1 : 0; } - else { + + if (pickNaN(aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN, + aIsLargerSignificand)) { res = bv; + } else { + res = av; } + return make_float32(res); } @@ -276,7 +318,7 @@ static float64 commonNaNToFloat64( commonNaNT a ) static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM) { - flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN, aIsLargerSignificand; bits64 av, bv, res; if ( STATUS(default_nan_mode) ) @@ -296,26 +338,22 @@ static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM) bv |= LIT64( 0x0008000000000000 ); #endif if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); - if ( aIsSignalingNaN ) { - if ( bIsSignalingNaN ) goto returnLargerSignificand; - res = bIsNaN ? bv : av; - } - else if ( aIsNaN ) { - if ( bIsSignalingNaN || ! bIsNaN ) - res = av; - else { - returnLargerSignificand: - if ( (bits64) ( av<<1 ) < (bits64) ( bv<<1 ) ) - res = bv; - else if ( (bits64) ( bv<<1 ) < (bits64) ( av<<1 ) ) - res = av; - else - res = ( av < bv ) ? av : bv; - } + + if ((bits64)(av<<1) < (bits64)(bv<<1)) { + aIsLargerSignificand = 0; + } else if ((bits64)(bv<<1) < (bits64)(av<<1)) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (av < bv) ? 1 : 0; } - else { + + if (pickNaN(aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN, + aIsLargerSignificand)) { res = bv; + } else { + res = av; } + return make_float64(res); } @@ -416,7 +454,7 @@ static floatx80 commonNaNToFloatx80( commonNaNT a ) static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM) { - flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN, aIsLargerSignificand; if ( STATUS(default_nan_mode) ) { a.low = floatx80_default_nan_low; @@ -436,19 +474,20 @@ static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM) b.low |= LIT64( 0xC000000000000000 ); #endif if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); - if ( aIsSignalingNaN ) { - if ( bIsSignalingNaN ) goto returnLargerSignificand; - return bIsNaN ? b : a; - } - else if ( aIsNaN ) { - if ( bIsSignalingNaN || ! bIsNaN ) return a; - returnLargerSignificand: - if ( a.low < b.low ) return b; - if ( b.low < a.low ) return a; - return ( a.high < b.high ) ? a : b; + + if (a.low < b.low) { + aIsLargerSignificand = 0; + } else if (b.low < a.low) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (a.high < b.high) ? 1 : 0; } - else { + + if (pickNaN(aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN, + aIsLargerSignificand)) { return b; + } else { + return a; } } @@ -542,7 +581,7 @@ static float128 commonNaNToFloat128( commonNaNT a ) static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM) { - flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN, aIsLargerSignificand; if ( STATUS(default_nan_mode) ) { a.low = float128_default_nan_low; @@ -562,19 +601,20 @@ static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM) b.high |= LIT64( 0x0000800000000000 ); #endif if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); - if ( aIsSignalingNaN ) { - if ( bIsSignalingNaN ) goto returnLargerSignificand; - return bIsNaN ? b : a; - } - else if ( aIsNaN ) { - if ( bIsSignalingNaN || ! bIsNaN ) return a; - returnLargerSignificand: - if ( lt128( a.high<<1, a.low, b.high<<1, b.low ) ) return b; - if ( lt128( b.high<<1, b.low, a.high<<1, a.low ) ) return a; - return ( a.high < b.high ) ? a : b; + + if (lt128(a.high<<1, a.low, b.high<<1, b.low)) { + aIsLargerSignificand = 0; + } else if (lt128(b.high<<1, b.low, a.high<<1, a.low)) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (a.high < b.high) ? 1 : 0; } - else { + + if (pickNaN(aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN, + aIsLargerSignificand)) { return b; + } else { + return a; } }