Loading fpu/softfloat-specialize.h +178 −0 Original line number Diff line number Diff line Loading @@ -419,6 +419,82 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, } #endif /*---------------------------------------------------------------------------- | Select which NaN to propagate for a three-input operation. | For the moment we assume that no CPU needs the 'larger significand' | information. | Return values : 0 : a; 1 : b; 2 : c; 3 : default-NaN *----------------------------------------------------------------------------*/ #if defined(TARGET_ARM) static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag cIsQNaN, flag cIsSNaN, flag infzero STATUS_PARAM) { /* For ARM, the (inf,zero,qnan) case sets InvalidOp and returns * the default NaN */ if (infzero && cIsQNaN) { float_raise(float_flag_invalid STATUS_VAR); return 3; } /* This looks different from the ARM ARM pseudocode, because the ARM ARM * puts the operands to a fused mac operation (a*b)+c in the order c,a,b. */ if (cIsSNaN) { return 2; } else if (aIsSNaN) { return 0; } else if (bIsSNaN) { return 1; } else if (cIsQNaN) { return 2; } else if (aIsQNaN) { return 0; } else { return 1; } } #elif defined(TARGET_PPC) static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag cIsQNaN, flag cIsSNaN, flag infzero STATUS_PARAM) { /* For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer * to return an input NaN if we have one (ie c) rather than generating * a default NaN */ if (infzero) { float_raise(float_flag_invalid STATUS_VAR); return 2; } /* If fRA is a NaN return it; otherwise if fRB is a NaN return it; * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB */ if (aIsSNaN || aIsQNaN) { return 0; } else if (cIsSNaN || cIsQNaN) { return 2; } else { return 1; } } #else /* A default implementation: prefer a to b to c. * This is unlikely to actually match any real implementation. */ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag cIsQNaN, flag cIsSNaN, flag infzero STATUS_PARAM) { if (aIsSNaN || aIsQNaN) { return 0; } else if (bIsSNaN || bIsQNaN) { return 1; } else { return 2; } } #endif /*---------------------------------------------------------------------------- | 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 Loading Loading @@ -459,6 +535,57 @@ static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM) } } /*---------------------------------------------------------------------------- | Takes three single-precision floating-point values `a', `b' and `c', one of | which is a NaN, and returns the appropriate NaN result. If any of `a', | `b' or `c' is a signaling NaN, the invalid exception is raised. | The input infzero indicates whether a*b was 0*inf or inf*0 (in which case | obviously c is a NaN, and whether to propagate c or some other NaN is | implementation defined). *----------------------------------------------------------------------------*/ static float32 propagateFloat32MulAddNaN(float32 a, float32 b, float32 c, flag infzero STATUS_PARAM) { flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, cIsQuietNaN, cIsSignalingNaN; int which; aIsQuietNaN = float32_is_quiet_nan(a); aIsSignalingNaN = float32_is_signaling_nan(a); bIsQuietNaN = float32_is_quiet_nan(b); bIsSignalingNaN = float32_is_signaling_nan(b); cIsQuietNaN = float32_is_quiet_nan(c); cIsSignalingNaN = float32_is_signaling_nan(c); if (aIsSignalingNaN | bIsSignalingNaN | cIsSignalingNaN) { float_raise(float_flag_invalid STATUS_VAR); } which = pickNaNMulAdd(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, cIsQuietNaN, cIsSignalingNaN, infzero STATUS_VAR); if (STATUS(default_nan_mode)) { /* Note that this check is after pickNaNMulAdd so that function * has an opportunity to set the Invalid flag. */ return float32_default_nan; } switch (which) { case 0: return float32_maybe_silence_nan(a); case 1: return float32_maybe_silence_nan(b); case 2: return float32_maybe_silence_nan(c); case 3: default: return float32_default_nan; } } /*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. Loading Loading @@ -595,6 +722,57 @@ static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM) } } /*---------------------------------------------------------------------------- | Takes three double-precision floating-point values `a', `b' and `c', one of | which is a NaN, and returns the appropriate NaN result. If any of `a', | `b' or `c' is a signaling NaN, the invalid exception is raised. | The input infzero indicates whether a*b was 0*inf or inf*0 (in which case | obviously c is a NaN, and whether to propagate c or some other NaN is | implementation defined). *----------------------------------------------------------------------------*/ static float64 propagateFloat64MulAddNaN(float64 a, float64 b, float64 c, flag infzero STATUS_PARAM) { flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, cIsQuietNaN, cIsSignalingNaN; int which; aIsQuietNaN = float64_is_quiet_nan(a); aIsSignalingNaN = float64_is_signaling_nan(a); bIsQuietNaN = float64_is_quiet_nan(b); bIsSignalingNaN = float64_is_signaling_nan(b); cIsQuietNaN = float64_is_quiet_nan(c); cIsSignalingNaN = float64_is_signaling_nan(c); if (aIsSignalingNaN | bIsSignalingNaN | cIsSignalingNaN) { float_raise(float_flag_invalid STATUS_VAR); } which = pickNaNMulAdd(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, cIsQuietNaN, cIsSignalingNaN, infzero STATUS_VAR); if (STATUS(default_nan_mode)) { /* Note that this check is after pickNaNMulAdd so that function * has an opportunity to set the Invalid flag. */ return float64_default_nan; } switch (which) { case 0: return float64_maybe_silence_nan(a); case 1: return float64_maybe_silence_nan(b); case 2: return float64_maybe_silence_nan(c); case 3: default: return float64_default_nan; } } /*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is a | quiet NaN; otherwise returns 0. This slightly differs from the same Loading fpu/softfloat.c +427 −0 Original line number Diff line number Diff line Loading @@ -2117,6 +2117,213 @@ float32 float32_rem( float32 a, float32 b STATUS_PARAM ) } /*---------------------------------------------------------------------------- | Returns the result of multiplying the single-precision floating-point values | `a' and `b' then adding 'c', with no intermediate rounding step after the | multiplication. The operation is performed according to the IEC/IEEE | Standard for Binary Floating-Point Arithmetic 754-2008. | The flags argument allows the caller to select negation of the | addend, the intermediate product, or the final result. (The difference | between this and having the caller do a separate negation is that negating | externally will flip the sign bit on NaNs.) *----------------------------------------------------------------------------*/ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) { flag aSign, bSign, cSign, zSign; int aExp, bExp, cExp, pExp, zExp, expDiff; uint32_t aSig, bSig, cSig; flag pInf, pZero, pSign; uint64_t pSig64, cSig64, zSig64; uint32_t pSig; int shiftcount; flag signflip, infzero; a = float32_squash_input_denormal(a STATUS_VAR); b = float32_squash_input_denormal(b STATUS_VAR); c = float32_squash_input_denormal(c STATUS_VAR); aSig = extractFloat32Frac(a); aExp = extractFloat32Exp(a); aSign = extractFloat32Sign(a); bSig = extractFloat32Frac(b); bExp = extractFloat32Exp(b); bSign = extractFloat32Sign(b); cSig = extractFloat32Frac(c); cExp = extractFloat32Exp(c); cSign = extractFloat32Sign(c); infzero = ((aExp == 0 && aSig == 0 && bExp == 0xff && bSig == 0) || (aExp == 0xff && aSig == 0 && bExp == 0 && bSig == 0)); /* It is implementation-defined whether the cases of (0,inf,qnan) * and (inf,0,qnan) raise InvalidOperation or not (and what QNaN * they return if they do), so we have to hand this information * off to the target-specific pick-a-NaN routine. */ if (((aExp == 0xff) && aSig) || ((bExp == 0xff) && bSig) || ((cExp == 0xff) && cSig)) { return propagateFloat32MulAddNaN(a, b, c, infzero STATUS_VAR); } if (infzero) { float_raise(float_flag_invalid STATUS_VAR); return float32_default_nan; } if (flags & float_muladd_negate_c) { cSign ^= 1; } signflip = (flags & float_muladd_negate_result) ? 1 : 0; /* Work out the sign and type of the product */ pSign = aSign ^ bSign; if (flags & float_muladd_negate_product) { pSign ^= 1; } pInf = (aExp == 0xff) || (bExp == 0xff); pZero = ((aExp | aSig) == 0) || ((bExp | bSig) == 0); if (cExp == 0xff) { if (pInf && (pSign ^ cSign)) { /* addition of opposite-signed infinities => InvalidOperation */ float_raise(float_flag_invalid STATUS_VAR); return float32_default_nan; } /* Otherwise generate an infinity of the same sign */ return packFloat32(cSign ^ signflip, 0xff, 0); } if (pInf) { return packFloat32(pSign ^ signflip, 0xff, 0); } if (pZero) { if (cExp == 0) { if (cSig == 0) { /* Adding two exact zeroes */ if (pSign == cSign) { zSign = pSign; } else if (STATUS(float_rounding_mode) == float_round_down) { zSign = 1; } else { zSign = 0; } return packFloat32(zSign ^ signflip, 0, 0); } /* Exact zero plus a denorm */ if (STATUS(flush_to_zero)) { float_raise(float_flag_output_denormal STATUS_VAR); return packFloat32(cSign ^ signflip, 0, 0); } } /* Zero plus something non-zero : just return the something */ return c ^ (signflip << 31); } if (aExp == 0) { normalizeFloat32Subnormal(aSig, &aExp, &aSig); } if (bExp == 0) { normalizeFloat32Subnormal(bSig, &bExp, &bSig); } /* Calculate the actual result a * b + c */ /* Multiply first; this is easy. */ /* NB: we subtract 0x7e where float32_mul() subtracts 0x7f * because we want the true exponent, not the "one-less-than" * flavour that roundAndPackFloat32() takes. */ pExp = aExp + bExp - 0x7e; aSig = (aSig | 0x00800000) << 7; bSig = (bSig | 0x00800000) << 8; pSig64 = (uint64_t)aSig * bSig; if ((int64_t)(pSig64 << 1) >= 0) { pSig64 <<= 1; pExp--; } zSign = pSign ^ signflip; /* Now pSig64 is the significand of the multiply, with the explicit bit in * position 62. */ if (cExp == 0) { if (!cSig) { /* Throw out the special case of c being an exact zero now */ shift64RightJamming(pSig64, 32, &pSig64); pSig = pSig64; return roundAndPackFloat32(zSign, pExp - 1, pSig STATUS_VAR); } normalizeFloat32Subnormal(cSig, &cExp, &cSig); } cSig64 = (uint64_t)cSig << (62 - 23); cSig64 |= LIT64(0x4000000000000000); expDiff = pExp - cExp; if (pSign == cSign) { /* Addition */ if (expDiff > 0) { /* scale c to match p */ shift64RightJamming(cSig64, expDiff, &cSig64); zExp = pExp; } else if (expDiff < 0) { /* scale p to match c */ shift64RightJamming(pSig64, -expDiff, &pSig64); zExp = cExp; } else { /* no scaling needed */ zExp = cExp; } /* Add significands and make sure explicit bit ends up in posn 62 */ zSig64 = pSig64 + cSig64; if ((int64_t)zSig64 < 0) { shift64RightJamming(zSig64, 1, &zSig64); } else { zExp--; } } else { /* Subtraction */ if (expDiff > 0) { shift64RightJamming(cSig64, expDiff, &cSig64); zSig64 = pSig64 - cSig64; zExp = pExp; } else if (expDiff < 0) { shift64RightJamming(pSig64, -expDiff, &pSig64); zSig64 = cSig64 - pSig64; zExp = cExp; zSign ^= 1; } else { zExp = pExp; if (cSig64 < pSig64) { zSig64 = pSig64 - cSig64; } else if (pSig64 < cSig64) { zSig64 = cSig64 - pSig64; zSign ^= 1; } else { /* Exact zero */ zSign = signflip; if (STATUS(float_rounding_mode) == float_round_down) { zSign ^= 1; } return packFloat32(zSign, 0, 0); } } --zExp; /* Normalize to put the explicit bit back into bit 62. */ shiftcount = countLeadingZeros64(zSig64) - 1; zSig64 <<= shiftcount; zExp -= shiftcount; } shift64RightJamming(zSig64, 32, &zSig64); return roundAndPackFloat32(zSign, zExp, zSig64 STATUS_VAR); } /*---------------------------------------------------------------------------- | Returns the square root of the single-precision floating-point value `a'. | The operation is performed according to the IEC/IEEE Standard for Binary Loading Loading @@ -3464,6 +3671,226 @@ float64 float64_rem( float64 a, float64 b STATUS_PARAM ) } /*---------------------------------------------------------------------------- | Returns the result of multiplying the double-precision floating-point values | `a' and `b' then adding 'c', with no intermediate rounding step after the | multiplication. The operation is performed according to the IEC/IEEE | Standard for Binary Floating-Point Arithmetic 754-2008. | The flags argument allows the caller to select negation of the | addend, the intermediate product, or the final result. (The difference | between this and having the caller do a separate negation is that negating | externally will flip the sign bit on NaNs.) *----------------------------------------------------------------------------*/ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) { flag aSign, bSign, cSign, zSign; int aExp, bExp, cExp, pExp, zExp, expDiff; uint64_t aSig, bSig, cSig; flag pInf, pZero, pSign; uint64_t pSig0, pSig1, cSig0, cSig1, zSig0, zSig1; int shiftcount; flag signflip, infzero; a = float64_squash_input_denormal(a STATUS_VAR); b = float64_squash_input_denormal(b STATUS_VAR); c = float64_squash_input_denormal(c STATUS_VAR); aSig = extractFloat64Frac(a); aExp = extractFloat64Exp(a); aSign = extractFloat64Sign(a); bSig = extractFloat64Frac(b); bExp = extractFloat64Exp(b); bSign = extractFloat64Sign(b); cSig = extractFloat64Frac(c); cExp = extractFloat64Exp(c); cSign = extractFloat64Sign(c); infzero = ((aExp == 0 && aSig == 0 && bExp == 0x7ff && bSig == 0) || (aExp == 0x7ff && aSig == 0 && bExp == 0 && bSig == 0)); /* It is implementation-defined whether the cases of (0,inf,qnan) * and (inf,0,qnan) raise InvalidOperation or not (and what QNaN * they return if they do), so we have to hand this information * off to the target-specific pick-a-NaN routine. */ if (((aExp == 0x7ff) && aSig) || ((bExp == 0x7ff) && bSig) || ((cExp == 0x7ff) && cSig)) { return propagateFloat64MulAddNaN(a, b, c, infzero STATUS_VAR); } if (infzero) { float_raise(float_flag_invalid STATUS_VAR); return float64_default_nan; } if (flags & float_muladd_negate_c) { cSign ^= 1; } signflip = (flags & float_muladd_negate_result) ? 1 : 0; /* Work out the sign and type of the product */ pSign = aSign ^ bSign; if (flags & float_muladd_negate_product) { pSign ^= 1; } pInf = (aExp == 0x7ff) || (bExp == 0x7ff); pZero = ((aExp | aSig) == 0) || ((bExp | bSig) == 0); if (cExp == 0x7ff) { if (pInf && (pSign ^ cSign)) { /* addition of opposite-signed infinities => InvalidOperation */ float_raise(float_flag_invalid STATUS_VAR); return float64_default_nan; } /* Otherwise generate an infinity of the same sign */ return packFloat64(cSign ^ signflip, 0x7ff, 0); } if (pInf) { return packFloat64(pSign ^ signflip, 0x7ff, 0); } if (pZero) { if (cExp == 0) { if (cSig == 0) { /* Adding two exact zeroes */ if (pSign == cSign) { zSign = pSign; } else if (STATUS(float_rounding_mode) == float_round_down) { zSign = 1; } else { zSign = 0; } return packFloat64(zSign ^ signflip, 0, 0); } /* Exact zero plus a denorm */ if (STATUS(flush_to_zero)) { float_raise(float_flag_output_denormal STATUS_VAR); return packFloat64(cSign ^ signflip, 0, 0); } } /* Zero plus something non-zero : just return the something */ return c ^ ((uint64_t)signflip << 63); } if (aExp == 0) { normalizeFloat64Subnormal(aSig, &aExp, &aSig); } if (bExp == 0) { normalizeFloat64Subnormal(bSig, &bExp, &bSig); } /* Calculate the actual result a * b + c */ /* Multiply first; this is easy. */ /* NB: we subtract 0x3fe where float64_mul() subtracts 0x3ff * because we want the true exponent, not the "one-less-than" * flavour that roundAndPackFloat64() takes. */ pExp = aExp + bExp - 0x3fe; aSig = (aSig | LIT64(0x0010000000000000))<<10; bSig = (bSig | LIT64(0x0010000000000000))<<11; mul64To128(aSig, bSig, &pSig0, &pSig1); if ((int64_t)(pSig0 << 1) >= 0) { shortShift128Left(pSig0, pSig1, 1, &pSig0, &pSig1); pExp--; } zSign = pSign ^ signflip; /* Now [pSig0:pSig1] is the significand of the multiply, with the explicit * bit in position 126. */ if (cExp == 0) { if (!cSig) { /* Throw out the special case of c being an exact zero now */ shift128RightJamming(pSig0, pSig1, 64, &pSig0, &pSig1); return roundAndPackFloat64(zSign, pExp - 1, pSig1 STATUS_VAR); } normalizeFloat64Subnormal(cSig, &cExp, &cSig); } /* Shift cSig and add the explicit bit so [cSig0:cSig1] is the * significand of the addend, with the explicit bit in position 126. */ cSig0 = cSig << (126 - 64 - 52); cSig1 = 0; cSig0 |= LIT64(0x4000000000000000); expDiff = pExp - cExp; if (pSign == cSign) { /* Addition */ if (expDiff > 0) { /* scale c to match p */ shift128RightJamming(cSig0, cSig1, expDiff, &cSig0, &cSig1); zExp = pExp; } else if (expDiff < 0) { /* scale p to match c */ shift128RightJamming(pSig0, pSig1, -expDiff, &pSig0, &pSig1); zExp = cExp; } else { /* no scaling needed */ zExp = cExp; } /* Add significands and make sure explicit bit ends up in posn 126 */ add128(pSig0, pSig1, cSig0, cSig1, &zSig0, &zSig1); if ((int64_t)zSig0 < 0) { shift128RightJamming(zSig0, zSig1, 1, &zSig0, &zSig1); } else { zExp--; } shift128RightJamming(zSig0, zSig1, 64, &zSig0, &zSig1); return roundAndPackFloat64(zSign, zExp, zSig1 STATUS_VAR); } else { /* Subtraction */ if (expDiff > 0) { shift128RightJamming(cSig0, cSig1, expDiff, &cSig0, &cSig1); sub128(pSig0, pSig1, cSig0, cSig1, &zSig0, &zSig1); zExp = pExp; } else if (expDiff < 0) { shift128RightJamming(pSig0, pSig1, -expDiff, &pSig0, &pSig1); sub128(cSig0, cSig1, pSig0, pSig1, &zSig0, &zSig1); zExp = cExp; zSign ^= 1; } else { zExp = pExp; if (lt128(cSig0, cSig1, pSig0, pSig1)) { sub128(pSig0, pSig1, cSig0, cSig1, &zSig0, &zSig1); } else if (lt128(pSig0, pSig1, cSig0, cSig1)) { sub128(cSig0, cSig1, pSig0, pSig1, &zSig0, &zSig1); zSign ^= 1; } else { /* Exact zero */ zSign = signflip; if (STATUS(float_rounding_mode) == float_round_down) { zSign ^= 1; } return packFloat64(zSign, 0, 0); } } --zExp; /* Do the equivalent of normalizeRoundAndPackFloat64() but * starting with the significand in a pair of uint64_t. */ if (zSig0) { shiftcount = countLeadingZeros64(zSig0) - 1; shortShift128Left(zSig0, zSig1, shiftcount, &zSig0, &zSig1); if (zSig1) { zSig0 |= 1; } zExp -= shiftcount; } else { shiftcount = countLeadingZeros64(zSig1) - 1; zSig0 = zSig1 << shiftcount; zExp -= (shiftcount + 64); } return roundAndPackFloat64(zSign, zExp, zSig0 STATUS_VAR); } } /*---------------------------------------------------------------------------- | Returns the square root of the double-precision floating-point value `a'. | The operation is performed according to the IEC/IEEE Standard for Binary Loading fpu/softfloat.h +14 −0 Original line number Diff line number Diff line Loading @@ -211,6 +211,18 @@ void set_floatx80_rounding_precision(int val STATUS_PARAM); *----------------------------------------------------------------------------*/ void float_raise( int8 flags STATUS_PARAM); /*---------------------------------------------------------------------------- | Options to indicate which negations to perform in float*_muladd() | Using these differs from negating an input or output before calling | the muladd function in that this means that a NaN doesn't have its | sign bit inverted before it is propagated. *----------------------------------------------------------------------------*/ enum { float_muladd_negate_c = 1, float_muladd_negate_product = 2, float_muladd_negate_result = 3, }; /*---------------------------------------------------------------------------- | Software IEC/IEEE integer-to-floating-point conversion routines. *----------------------------------------------------------------------------*/ Loading Loading @@ -269,6 +281,7 @@ float32 float32_sub( float32, float32 STATUS_PARAM ); float32 float32_mul( float32, float32 STATUS_PARAM ); float32 float32_div( float32, float32 STATUS_PARAM ); float32 float32_rem( float32, float32 STATUS_PARAM ); float32 float32_muladd(float32, float32, float32, int STATUS_PARAM); float32 float32_sqrt( float32 STATUS_PARAM ); float32 float32_exp2( float32 STATUS_PARAM ); float32 float32_log2( float32 STATUS_PARAM ); Loading Loading @@ -375,6 +388,7 @@ float64 float64_sub( float64, float64 STATUS_PARAM ); float64 float64_mul( float64, float64 STATUS_PARAM ); float64 float64_div( float64, float64 STATUS_PARAM ); float64 float64_rem( float64, float64 STATUS_PARAM ); float64 float64_muladd(float64, float64, float64, int STATUS_PARAM); float64 float64_sqrt( float64 STATUS_PARAM ); float64 float64_log2( float64 STATUS_PARAM ); int float64_eq( float64, float64 STATUS_PARAM ); Loading target-arm/cpu.h +3 −1 Original line number Diff line number Diff line Loading @@ -366,7 +366,7 @@ enum arm_features { ARM_FEATURE_VFP3, ARM_FEATURE_VFP_FP16, ARM_FEATURE_NEON, ARM_FEATURE_DIV, ARM_FEATURE_THUMB_DIV, /* divide supported in Thumb encoding */ ARM_FEATURE_M, /* Microcontroller profile. */ ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */ ARM_FEATURE_THUMB2EE, Loading @@ -375,6 +375,8 @@ enum arm_features { ARM_FEATURE_V5, ARM_FEATURE_STRONGARM, ARM_FEATURE_VAPA, /* cp15 VA to PA lookups */ ARM_FEATURE_ARM_DIV, /* divide supported in ARM encoding */ ARM_FEATURE_VFP4, /* VFPv4 (implies that NEON is v2) */ }; static inline int arm_feature(CPUARMState *env, int feature) Loading target-arm/helper.c +21 −5 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
fpu/softfloat-specialize.h +178 −0 Original line number Diff line number Diff line Loading @@ -419,6 +419,82 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, } #endif /*---------------------------------------------------------------------------- | Select which NaN to propagate for a three-input operation. | For the moment we assume that no CPU needs the 'larger significand' | information. | Return values : 0 : a; 1 : b; 2 : c; 3 : default-NaN *----------------------------------------------------------------------------*/ #if defined(TARGET_ARM) static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag cIsQNaN, flag cIsSNaN, flag infzero STATUS_PARAM) { /* For ARM, the (inf,zero,qnan) case sets InvalidOp and returns * the default NaN */ if (infzero && cIsQNaN) { float_raise(float_flag_invalid STATUS_VAR); return 3; } /* This looks different from the ARM ARM pseudocode, because the ARM ARM * puts the operands to a fused mac operation (a*b)+c in the order c,a,b. */ if (cIsSNaN) { return 2; } else if (aIsSNaN) { return 0; } else if (bIsSNaN) { return 1; } else if (cIsQNaN) { return 2; } else if (aIsQNaN) { return 0; } else { return 1; } } #elif defined(TARGET_PPC) static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag cIsQNaN, flag cIsSNaN, flag infzero STATUS_PARAM) { /* For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer * to return an input NaN if we have one (ie c) rather than generating * a default NaN */ if (infzero) { float_raise(float_flag_invalid STATUS_VAR); return 2; } /* If fRA is a NaN return it; otherwise if fRB is a NaN return it; * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB */ if (aIsSNaN || aIsQNaN) { return 0; } else if (cIsSNaN || cIsQNaN) { return 2; } else { return 1; } } #else /* A default implementation: prefer a to b to c. * This is unlikely to actually match any real implementation. */ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag cIsQNaN, flag cIsSNaN, flag infzero STATUS_PARAM) { if (aIsSNaN || aIsQNaN) { return 0; } else if (bIsSNaN || bIsQNaN) { return 1; } else { return 2; } } #endif /*---------------------------------------------------------------------------- | 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 Loading Loading @@ -459,6 +535,57 @@ static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM) } } /*---------------------------------------------------------------------------- | Takes three single-precision floating-point values `a', `b' and `c', one of | which is a NaN, and returns the appropriate NaN result. If any of `a', | `b' or `c' is a signaling NaN, the invalid exception is raised. | The input infzero indicates whether a*b was 0*inf or inf*0 (in which case | obviously c is a NaN, and whether to propagate c or some other NaN is | implementation defined). *----------------------------------------------------------------------------*/ static float32 propagateFloat32MulAddNaN(float32 a, float32 b, float32 c, flag infzero STATUS_PARAM) { flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, cIsQuietNaN, cIsSignalingNaN; int which; aIsQuietNaN = float32_is_quiet_nan(a); aIsSignalingNaN = float32_is_signaling_nan(a); bIsQuietNaN = float32_is_quiet_nan(b); bIsSignalingNaN = float32_is_signaling_nan(b); cIsQuietNaN = float32_is_quiet_nan(c); cIsSignalingNaN = float32_is_signaling_nan(c); if (aIsSignalingNaN | bIsSignalingNaN | cIsSignalingNaN) { float_raise(float_flag_invalid STATUS_VAR); } which = pickNaNMulAdd(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, cIsQuietNaN, cIsSignalingNaN, infzero STATUS_VAR); if (STATUS(default_nan_mode)) { /* Note that this check is after pickNaNMulAdd so that function * has an opportunity to set the Invalid flag. */ return float32_default_nan; } switch (which) { case 0: return float32_maybe_silence_nan(a); case 1: return float32_maybe_silence_nan(b); case 2: return float32_maybe_silence_nan(c); case 3: default: return float32_default_nan; } } /*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. Loading Loading @@ -595,6 +722,57 @@ static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM) } } /*---------------------------------------------------------------------------- | Takes three double-precision floating-point values `a', `b' and `c', one of | which is a NaN, and returns the appropriate NaN result. If any of `a', | `b' or `c' is a signaling NaN, the invalid exception is raised. | The input infzero indicates whether a*b was 0*inf or inf*0 (in which case | obviously c is a NaN, and whether to propagate c or some other NaN is | implementation defined). *----------------------------------------------------------------------------*/ static float64 propagateFloat64MulAddNaN(float64 a, float64 b, float64 c, flag infzero STATUS_PARAM) { flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, cIsQuietNaN, cIsSignalingNaN; int which; aIsQuietNaN = float64_is_quiet_nan(a); aIsSignalingNaN = float64_is_signaling_nan(a); bIsQuietNaN = float64_is_quiet_nan(b); bIsSignalingNaN = float64_is_signaling_nan(b); cIsQuietNaN = float64_is_quiet_nan(c); cIsSignalingNaN = float64_is_signaling_nan(c); if (aIsSignalingNaN | bIsSignalingNaN | cIsSignalingNaN) { float_raise(float_flag_invalid STATUS_VAR); } which = pickNaNMulAdd(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, cIsQuietNaN, cIsSignalingNaN, infzero STATUS_VAR); if (STATUS(default_nan_mode)) { /* Note that this check is after pickNaNMulAdd so that function * has an opportunity to set the Invalid flag. */ return float64_default_nan; } switch (which) { case 0: return float64_maybe_silence_nan(a); case 1: return float64_maybe_silence_nan(b); case 2: return float64_maybe_silence_nan(c); case 3: default: return float64_default_nan; } } /*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is a | quiet NaN; otherwise returns 0. This slightly differs from the same Loading
fpu/softfloat.c +427 −0 Original line number Diff line number Diff line Loading @@ -2117,6 +2117,213 @@ float32 float32_rem( float32 a, float32 b STATUS_PARAM ) } /*---------------------------------------------------------------------------- | Returns the result of multiplying the single-precision floating-point values | `a' and `b' then adding 'c', with no intermediate rounding step after the | multiplication. The operation is performed according to the IEC/IEEE | Standard for Binary Floating-Point Arithmetic 754-2008. | The flags argument allows the caller to select negation of the | addend, the intermediate product, or the final result. (The difference | between this and having the caller do a separate negation is that negating | externally will flip the sign bit on NaNs.) *----------------------------------------------------------------------------*/ float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) { flag aSign, bSign, cSign, zSign; int aExp, bExp, cExp, pExp, zExp, expDiff; uint32_t aSig, bSig, cSig; flag pInf, pZero, pSign; uint64_t pSig64, cSig64, zSig64; uint32_t pSig; int shiftcount; flag signflip, infzero; a = float32_squash_input_denormal(a STATUS_VAR); b = float32_squash_input_denormal(b STATUS_VAR); c = float32_squash_input_denormal(c STATUS_VAR); aSig = extractFloat32Frac(a); aExp = extractFloat32Exp(a); aSign = extractFloat32Sign(a); bSig = extractFloat32Frac(b); bExp = extractFloat32Exp(b); bSign = extractFloat32Sign(b); cSig = extractFloat32Frac(c); cExp = extractFloat32Exp(c); cSign = extractFloat32Sign(c); infzero = ((aExp == 0 && aSig == 0 && bExp == 0xff && bSig == 0) || (aExp == 0xff && aSig == 0 && bExp == 0 && bSig == 0)); /* It is implementation-defined whether the cases of (0,inf,qnan) * and (inf,0,qnan) raise InvalidOperation or not (and what QNaN * they return if they do), so we have to hand this information * off to the target-specific pick-a-NaN routine. */ if (((aExp == 0xff) && aSig) || ((bExp == 0xff) && bSig) || ((cExp == 0xff) && cSig)) { return propagateFloat32MulAddNaN(a, b, c, infzero STATUS_VAR); } if (infzero) { float_raise(float_flag_invalid STATUS_VAR); return float32_default_nan; } if (flags & float_muladd_negate_c) { cSign ^= 1; } signflip = (flags & float_muladd_negate_result) ? 1 : 0; /* Work out the sign and type of the product */ pSign = aSign ^ bSign; if (flags & float_muladd_negate_product) { pSign ^= 1; } pInf = (aExp == 0xff) || (bExp == 0xff); pZero = ((aExp | aSig) == 0) || ((bExp | bSig) == 0); if (cExp == 0xff) { if (pInf && (pSign ^ cSign)) { /* addition of opposite-signed infinities => InvalidOperation */ float_raise(float_flag_invalid STATUS_VAR); return float32_default_nan; } /* Otherwise generate an infinity of the same sign */ return packFloat32(cSign ^ signflip, 0xff, 0); } if (pInf) { return packFloat32(pSign ^ signflip, 0xff, 0); } if (pZero) { if (cExp == 0) { if (cSig == 0) { /* Adding two exact zeroes */ if (pSign == cSign) { zSign = pSign; } else if (STATUS(float_rounding_mode) == float_round_down) { zSign = 1; } else { zSign = 0; } return packFloat32(zSign ^ signflip, 0, 0); } /* Exact zero plus a denorm */ if (STATUS(flush_to_zero)) { float_raise(float_flag_output_denormal STATUS_VAR); return packFloat32(cSign ^ signflip, 0, 0); } } /* Zero plus something non-zero : just return the something */ return c ^ (signflip << 31); } if (aExp == 0) { normalizeFloat32Subnormal(aSig, &aExp, &aSig); } if (bExp == 0) { normalizeFloat32Subnormal(bSig, &bExp, &bSig); } /* Calculate the actual result a * b + c */ /* Multiply first; this is easy. */ /* NB: we subtract 0x7e where float32_mul() subtracts 0x7f * because we want the true exponent, not the "one-less-than" * flavour that roundAndPackFloat32() takes. */ pExp = aExp + bExp - 0x7e; aSig = (aSig | 0x00800000) << 7; bSig = (bSig | 0x00800000) << 8; pSig64 = (uint64_t)aSig * bSig; if ((int64_t)(pSig64 << 1) >= 0) { pSig64 <<= 1; pExp--; } zSign = pSign ^ signflip; /* Now pSig64 is the significand of the multiply, with the explicit bit in * position 62. */ if (cExp == 0) { if (!cSig) { /* Throw out the special case of c being an exact zero now */ shift64RightJamming(pSig64, 32, &pSig64); pSig = pSig64; return roundAndPackFloat32(zSign, pExp - 1, pSig STATUS_VAR); } normalizeFloat32Subnormal(cSig, &cExp, &cSig); } cSig64 = (uint64_t)cSig << (62 - 23); cSig64 |= LIT64(0x4000000000000000); expDiff = pExp - cExp; if (pSign == cSign) { /* Addition */ if (expDiff > 0) { /* scale c to match p */ shift64RightJamming(cSig64, expDiff, &cSig64); zExp = pExp; } else if (expDiff < 0) { /* scale p to match c */ shift64RightJamming(pSig64, -expDiff, &pSig64); zExp = cExp; } else { /* no scaling needed */ zExp = cExp; } /* Add significands and make sure explicit bit ends up in posn 62 */ zSig64 = pSig64 + cSig64; if ((int64_t)zSig64 < 0) { shift64RightJamming(zSig64, 1, &zSig64); } else { zExp--; } } else { /* Subtraction */ if (expDiff > 0) { shift64RightJamming(cSig64, expDiff, &cSig64); zSig64 = pSig64 - cSig64; zExp = pExp; } else if (expDiff < 0) { shift64RightJamming(pSig64, -expDiff, &pSig64); zSig64 = cSig64 - pSig64; zExp = cExp; zSign ^= 1; } else { zExp = pExp; if (cSig64 < pSig64) { zSig64 = pSig64 - cSig64; } else if (pSig64 < cSig64) { zSig64 = cSig64 - pSig64; zSign ^= 1; } else { /* Exact zero */ zSign = signflip; if (STATUS(float_rounding_mode) == float_round_down) { zSign ^= 1; } return packFloat32(zSign, 0, 0); } } --zExp; /* Normalize to put the explicit bit back into bit 62. */ shiftcount = countLeadingZeros64(zSig64) - 1; zSig64 <<= shiftcount; zExp -= shiftcount; } shift64RightJamming(zSig64, 32, &zSig64); return roundAndPackFloat32(zSign, zExp, zSig64 STATUS_VAR); } /*---------------------------------------------------------------------------- | Returns the square root of the single-precision floating-point value `a'. | The operation is performed according to the IEC/IEEE Standard for Binary Loading Loading @@ -3464,6 +3671,226 @@ float64 float64_rem( float64 a, float64 b STATUS_PARAM ) } /*---------------------------------------------------------------------------- | Returns the result of multiplying the double-precision floating-point values | `a' and `b' then adding 'c', with no intermediate rounding step after the | multiplication. The operation is performed according to the IEC/IEEE | Standard for Binary Floating-Point Arithmetic 754-2008. | The flags argument allows the caller to select negation of the | addend, the intermediate product, or the final result. (The difference | between this and having the caller do a separate negation is that negating | externally will flip the sign bit on NaNs.) *----------------------------------------------------------------------------*/ float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) { flag aSign, bSign, cSign, zSign; int aExp, bExp, cExp, pExp, zExp, expDiff; uint64_t aSig, bSig, cSig; flag pInf, pZero, pSign; uint64_t pSig0, pSig1, cSig0, cSig1, zSig0, zSig1; int shiftcount; flag signflip, infzero; a = float64_squash_input_denormal(a STATUS_VAR); b = float64_squash_input_denormal(b STATUS_VAR); c = float64_squash_input_denormal(c STATUS_VAR); aSig = extractFloat64Frac(a); aExp = extractFloat64Exp(a); aSign = extractFloat64Sign(a); bSig = extractFloat64Frac(b); bExp = extractFloat64Exp(b); bSign = extractFloat64Sign(b); cSig = extractFloat64Frac(c); cExp = extractFloat64Exp(c); cSign = extractFloat64Sign(c); infzero = ((aExp == 0 && aSig == 0 && bExp == 0x7ff && bSig == 0) || (aExp == 0x7ff && aSig == 0 && bExp == 0 && bSig == 0)); /* It is implementation-defined whether the cases of (0,inf,qnan) * and (inf,0,qnan) raise InvalidOperation or not (and what QNaN * they return if they do), so we have to hand this information * off to the target-specific pick-a-NaN routine. */ if (((aExp == 0x7ff) && aSig) || ((bExp == 0x7ff) && bSig) || ((cExp == 0x7ff) && cSig)) { return propagateFloat64MulAddNaN(a, b, c, infzero STATUS_VAR); } if (infzero) { float_raise(float_flag_invalid STATUS_VAR); return float64_default_nan; } if (flags & float_muladd_negate_c) { cSign ^= 1; } signflip = (flags & float_muladd_negate_result) ? 1 : 0; /* Work out the sign and type of the product */ pSign = aSign ^ bSign; if (flags & float_muladd_negate_product) { pSign ^= 1; } pInf = (aExp == 0x7ff) || (bExp == 0x7ff); pZero = ((aExp | aSig) == 0) || ((bExp | bSig) == 0); if (cExp == 0x7ff) { if (pInf && (pSign ^ cSign)) { /* addition of opposite-signed infinities => InvalidOperation */ float_raise(float_flag_invalid STATUS_VAR); return float64_default_nan; } /* Otherwise generate an infinity of the same sign */ return packFloat64(cSign ^ signflip, 0x7ff, 0); } if (pInf) { return packFloat64(pSign ^ signflip, 0x7ff, 0); } if (pZero) { if (cExp == 0) { if (cSig == 0) { /* Adding two exact zeroes */ if (pSign == cSign) { zSign = pSign; } else if (STATUS(float_rounding_mode) == float_round_down) { zSign = 1; } else { zSign = 0; } return packFloat64(zSign ^ signflip, 0, 0); } /* Exact zero plus a denorm */ if (STATUS(flush_to_zero)) { float_raise(float_flag_output_denormal STATUS_VAR); return packFloat64(cSign ^ signflip, 0, 0); } } /* Zero plus something non-zero : just return the something */ return c ^ ((uint64_t)signflip << 63); } if (aExp == 0) { normalizeFloat64Subnormal(aSig, &aExp, &aSig); } if (bExp == 0) { normalizeFloat64Subnormal(bSig, &bExp, &bSig); } /* Calculate the actual result a * b + c */ /* Multiply first; this is easy. */ /* NB: we subtract 0x3fe where float64_mul() subtracts 0x3ff * because we want the true exponent, not the "one-less-than" * flavour that roundAndPackFloat64() takes. */ pExp = aExp + bExp - 0x3fe; aSig = (aSig | LIT64(0x0010000000000000))<<10; bSig = (bSig | LIT64(0x0010000000000000))<<11; mul64To128(aSig, bSig, &pSig0, &pSig1); if ((int64_t)(pSig0 << 1) >= 0) { shortShift128Left(pSig0, pSig1, 1, &pSig0, &pSig1); pExp--; } zSign = pSign ^ signflip; /* Now [pSig0:pSig1] is the significand of the multiply, with the explicit * bit in position 126. */ if (cExp == 0) { if (!cSig) { /* Throw out the special case of c being an exact zero now */ shift128RightJamming(pSig0, pSig1, 64, &pSig0, &pSig1); return roundAndPackFloat64(zSign, pExp - 1, pSig1 STATUS_VAR); } normalizeFloat64Subnormal(cSig, &cExp, &cSig); } /* Shift cSig and add the explicit bit so [cSig0:cSig1] is the * significand of the addend, with the explicit bit in position 126. */ cSig0 = cSig << (126 - 64 - 52); cSig1 = 0; cSig0 |= LIT64(0x4000000000000000); expDiff = pExp - cExp; if (pSign == cSign) { /* Addition */ if (expDiff > 0) { /* scale c to match p */ shift128RightJamming(cSig0, cSig1, expDiff, &cSig0, &cSig1); zExp = pExp; } else if (expDiff < 0) { /* scale p to match c */ shift128RightJamming(pSig0, pSig1, -expDiff, &pSig0, &pSig1); zExp = cExp; } else { /* no scaling needed */ zExp = cExp; } /* Add significands and make sure explicit bit ends up in posn 126 */ add128(pSig0, pSig1, cSig0, cSig1, &zSig0, &zSig1); if ((int64_t)zSig0 < 0) { shift128RightJamming(zSig0, zSig1, 1, &zSig0, &zSig1); } else { zExp--; } shift128RightJamming(zSig0, zSig1, 64, &zSig0, &zSig1); return roundAndPackFloat64(zSign, zExp, zSig1 STATUS_VAR); } else { /* Subtraction */ if (expDiff > 0) { shift128RightJamming(cSig0, cSig1, expDiff, &cSig0, &cSig1); sub128(pSig0, pSig1, cSig0, cSig1, &zSig0, &zSig1); zExp = pExp; } else if (expDiff < 0) { shift128RightJamming(pSig0, pSig1, -expDiff, &pSig0, &pSig1); sub128(cSig0, cSig1, pSig0, pSig1, &zSig0, &zSig1); zExp = cExp; zSign ^= 1; } else { zExp = pExp; if (lt128(cSig0, cSig1, pSig0, pSig1)) { sub128(pSig0, pSig1, cSig0, cSig1, &zSig0, &zSig1); } else if (lt128(pSig0, pSig1, cSig0, cSig1)) { sub128(cSig0, cSig1, pSig0, pSig1, &zSig0, &zSig1); zSign ^= 1; } else { /* Exact zero */ zSign = signflip; if (STATUS(float_rounding_mode) == float_round_down) { zSign ^= 1; } return packFloat64(zSign, 0, 0); } } --zExp; /* Do the equivalent of normalizeRoundAndPackFloat64() but * starting with the significand in a pair of uint64_t. */ if (zSig0) { shiftcount = countLeadingZeros64(zSig0) - 1; shortShift128Left(zSig0, zSig1, shiftcount, &zSig0, &zSig1); if (zSig1) { zSig0 |= 1; } zExp -= shiftcount; } else { shiftcount = countLeadingZeros64(zSig1) - 1; zSig0 = zSig1 << shiftcount; zExp -= (shiftcount + 64); } return roundAndPackFloat64(zSign, zExp, zSig0 STATUS_VAR); } } /*---------------------------------------------------------------------------- | Returns the square root of the double-precision floating-point value `a'. | The operation is performed according to the IEC/IEEE Standard for Binary Loading
fpu/softfloat.h +14 −0 Original line number Diff line number Diff line Loading @@ -211,6 +211,18 @@ void set_floatx80_rounding_precision(int val STATUS_PARAM); *----------------------------------------------------------------------------*/ void float_raise( int8 flags STATUS_PARAM); /*---------------------------------------------------------------------------- | Options to indicate which negations to perform in float*_muladd() | Using these differs from negating an input or output before calling | the muladd function in that this means that a NaN doesn't have its | sign bit inverted before it is propagated. *----------------------------------------------------------------------------*/ enum { float_muladd_negate_c = 1, float_muladd_negate_product = 2, float_muladd_negate_result = 3, }; /*---------------------------------------------------------------------------- | Software IEC/IEEE integer-to-floating-point conversion routines. *----------------------------------------------------------------------------*/ Loading Loading @@ -269,6 +281,7 @@ float32 float32_sub( float32, float32 STATUS_PARAM ); float32 float32_mul( float32, float32 STATUS_PARAM ); float32 float32_div( float32, float32 STATUS_PARAM ); float32 float32_rem( float32, float32 STATUS_PARAM ); float32 float32_muladd(float32, float32, float32, int STATUS_PARAM); float32 float32_sqrt( float32 STATUS_PARAM ); float32 float32_exp2( float32 STATUS_PARAM ); float32 float32_log2( float32 STATUS_PARAM ); Loading Loading @@ -375,6 +388,7 @@ float64 float64_sub( float64, float64 STATUS_PARAM ); float64 float64_mul( float64, float64 STATUS_PARAM ); float64 float64_div( float64, float64 STATUS_PARAM ); float64 float64_rem( float64, float64 STATUS_PARAM ); float64 float64_muladd(float64, float64, float64, int STATUS_PARAM); float64 float64_sqrt( float64 STATUS_PARAM ); float64 float64_log2( float64 STATUS_PARAM ); int float64_eq( float64, float64 STATUS_PARAM ); Loading
target-arm/cpu.h +3 −1 Original line number Diff line number Diff line Loading @@ -366,7 +366,7 @@ enum arm_features { ARM_FEATURE_VFP3, ARM_FEATURE_VFP_FP16, ARM_FEATURE_NEON, ARM_FEATURE_DIV, ARM_FEATURE_THUMB_DIV, /* divide supported in Thumb encoding */ ARM_FEATURE_M, /* Microcontroller profile. */ ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */ ARM_FEATURE_THUMB2EE, Loading @@ -375,6 +375,8 @@ enum arm_features { ARM_FEATURE_V5, ARM_FEATURE_STRONGARM, ARM_FEATURE_VAPA, /* cp15 VA to PA lookups */ ARM_FEATURE_ARM_DIV, /* divide supported in ARM encoding */ ARM_FEATURE_VFP4, /* VFPv4 (implies that NEON is v2) */ }; static inline int arm_feature(CPUARMState *env, int feature) Loading