From 4f5bcdbdf334ba80e3f4890bb752e457601f3e9e Mon Sep 17 00:00:00 2001 From: jsm28 Date: Tue, 22 Nov 2005 00:38:30 +0000 Subject: * config/fp-bit.c (clzusi): New function. (si_to_float, usi_to_float): Use it to compute proper shift. (usi_to_float): Preserve guard bits when shifting right. * libgcc-std.ver (GCC_4.2.0): New version. * libgcc2.c (__floatundixf, __floatunditf, __floatundidf, __floatundisf): New functions. * libgcc2.h (__floatundixf, __floatunditf, __floatundidf, __floatundisf): Declare. * mklibgcc.in (lib2funcs): Add _floatundidf, _floatundisf, _floatundixf, and _floatunditf. * optabs.c (expand_float): If target does not define a pattern for signed or unsigned conversion, use an unsigned libcall instead of a signed one. (init_optabs): Initialize ufloat_optab. testsuite: * gcc.c-torture/execute/floatunsisf-1.c: New test. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@107345 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 17 +++ gcc/config/fp-bit.c | 50 ++++++--- gcc/libgcc-std.ver | 17 +++ gcc/libgcc2.c | 114 +++++++++++++++++++++ gcc/libgcc2.h | 8 ++ gcc/mklibgcc.in | 2 +- gcc/optabs.c | 15 ++- gcc/testsuite/ChangeLog | 4 + .../gcc.c-torture/execute/floatunsisf-1.c | 21 ++++ 9 files changed, 231 insertions(+), 17 deletions(-) create mode 100644 gcc/testsuite/gcc.c-torture/execute/floatunsisf-1.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1adc088a2ab..0fa5ae51e40 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,20 @@ +2005-11-22 Joseph S. Myers + + * config/fp-bit.c (clzusi): New function. + (si_to_float, usi_to_float): Use it to compute proper shift. + (usi_to_float): Preserve guard bits when shifting right. + * libgcc-std.ver (GCC_4.2.0): New version. + * libgcc2.c (__floatundixf, __floatunditf, __floatundidf, + __floatundisf): New functions. + * libgcc2.h (__floatundixf, __floatunditf, __floatundidf, + __floatundisf): Declare. + * mklibgcc.in (lib2funcs): Add _floatundidf, _floatundisf, + _floatundixf, and _floatunditf. + * optabs.c (expand_float): If target does not define a pattern for + signed or unsigned conversion, use an unsigned libcall instead of + a signed one. + (init_optabs): Initialize ufloat_optab. + 2005-11-22 Joseph S. Myers * config/rs6000/rs6000.opt (mmulhw): New option. diff --git a/gcc/config/fp-bit.c b/gcc/config/fp-bit.c index 4c637cb70ae..ccf927e8c3b 100644 --- a/gcc/config/fp-bit.c +++ b/gcc/config/fp-bit.c @@ -186,6 +186,22 @@ flip_sign ( fp_number_type * x) x->sign = !x->sign; } +/* Count leading zeroes in N. */ +INLINE +static int +clzusi (USItype n) +{ + extern int __clzsi2 (USItype); + if (sizeof (USItype) == sizeof (unsigned int)) + return __builtin_clz (n); + else if (sizeof (USItype) == sizeof (unsigned long)) + return __builtin_clzl (n); + else if (sizeof (USItype) == sizeof (unsigned long long)) + return __builtin_clzll (n); + else + return __clzsi2 (n); +} + extern FLO_type pack_d ( fp_number_type * ); #if defined(L_pack_df) || defined(L_pack_sf) || defined(L_pack_tf) @@ -1334,6 +1350,8 @@ si_to_float (SItype arg_a) } else { + USItype uarg; + int shift; in.normal_exp = FRACBITS + NGARDS; if (in.sign) { @@ -1343,15 +1361,17 @@ si_to_float (SItype arg_a) { return (FLO_type)(- MAX_SI_INT - 1); } - in.fraction.ll = (-arg_a); + uarg = (-arg_a); } else - in.fraction.ll = arg_a; + uarg = arg_a; - while (in.fraction.ll < ((fractype)1 << (FRACBITS + NGARDS))) + in.fraction.ll = uarg; + shift = clzusi (uarg) - (BITS_PER_SI - 1 - FRACBITS - NGARDS); + if (shift > 0) { - in.fraction.ll <<= 1; - in.normal_exp -= 1; + in.fraction.ll <<= shift; + in.normal_exp -= shift; } } return pack_d (&in); @@ -1371,19 +1391,23 @@ usi_to_float (USItype arg_a) } else { + int shift; in.class = CLASS_NUMBER; in.normal_exp = FRACBITS + NGARDS; in.fraction.ll = arg_a; - while (in.fraction.ll > ((fractype)1 << (FRACBITS + NGARDS))) - { - in.fraction.ll >>= 1; - in.normal_exp += 1; - } - while (in.fraction.ll < ((fractype)1 << (FRACBITS + NGARDS))) + shift = clzusi (arg_a) - (BITS_PER_SI - 1 - FRACBITS - NGARDS); + if (shift < 0) + { + fractype guard = in.fraction.ll & (((fractype)1 << -shift) - 1); + in.fraction.ll >>= -shift; + in.fraction.ll |= (guard != 0); + in.normal_exp -= shift; + } + else if (shift > 0) { - in.fraction.ll <<= 1; - in.normal_exp -= 1; + in.fraction.ll <<= shift; + in.normal_exp -= shift; } } return pack_d (&in); diff --git a/gcc/libgcc-std.ver b/gcc/libgcc-std.ver index 341cf7a481b..a98ddb463a2 100644 --- a/gcc/libgcc-std.ver +++ b/gcc/libgcc-std.ver @@ -252,3 +252,20 @@ GCC_4.0.0 { __mulxc3 __multc3 } + +%inherit GCC_4.2.0 GCC_4.0.0 +GCC_4.2.0 { + # unsigned-to-floating conversions + __floatunsisf + __floatunsidf + __floatunsixf + __floatunsitf + __floatundidf + __floatundisf + __floatundixf + __floatunditf + __floatuntidf + __floatuntisf + __floatuntixf + __floatuntitf +} diff --git a/gcc/libgcc2.c b/gcc/libgcc2.c index 8838c308c7f..94f4b30006a 100644 --- a/gcc/libgcc2.c +++ b/gcc/libgcc2.c @@ -1323,6 +1323,17 @@ __floatdixf (DWtype u) } #endif +#if defined(L_floatundixf) && LIBGCC2_HAS_XF_MODE +XFtype +__floatundixf (UDWtype u) +{ + XFtype d = (UWtype) (u >> W_TYPE_SIZE); + d *= Wtype_MAXp1_F; + d += (UWtype)u; + return d; +} +#endif + #if defined(L_floatditf) && LIBGCC2_HAS_TF_MODE TFtype __floatditf (DWtype u) @@ -1334,6 +1345,17 @@ __floatditf (DWtype u) } #endif +#if defined(L_floatunditf) && LIBGCC2_HAS_TF_MODE +TFtype +__floatunditf (UDWtype u) +{ + TFtype d = (UWtype) (u >> W_TYPE_SIZE); + d *= Wtype_MAXp1_F; + d += (UWtype)u; + return d; +} +#endif + #if defined(L_floatdidf) && LIBGCC2_HAS_DF_MODE DFtype __floatdidf (DWtype u) @@ -1345,6 +1367,17 @@ __floatdidf (DWtype u) } #endif +#if defined(L_floatundidf) && LIBGCC2_HAS_DF_MODE +DFtype +__floatundidf (UDWtype u) +{ + DFtype d = (UWtype) (u >> W_TYPE_SIZE); + d *= Wtype_MAXp1_F; + d += (UWtype)u; + return d; +} +#endif + #if defined(L_floatdisf) && LIBGCC2_HAS_SF_MODE #define DI_SIZE (W_TYPE_SIZE * 2) #define SF_SIZE FLT_MANT_DIG @@ -1433,6 +1466,87 @@ __floatdisf (DWtype u) } #endif +#if defined(L_floatundisf) && LIBGCC2_HAS_SF_MODE +#define DI_SIZE (W_TYPE_SIZE * 2) +#define SF_SIZE FLT_MANT_DIG + +SFtype +__floatundisf (UDWtype u) +{ +#if SF_SIZE >= W_TYPE_SIZE + /* When the word size is small, we never get any rounding error. */ + SFtype f = (UWtype) (u >> W_TYPE_SIZE); + f *= Wtype_MAXp1_F; + f += (UWtype)u; + return f; +#elif LIBGCC2_HAS_DF_MODE + +#if LIBGCC2_DOUBLE_TYPE_SIZE == 64 +#define DF_SIZE DBL_MANT_DIG +#elif LIBGCC2_LONG_DOUBLE_TYPE_SIZE == 64 +#define DF_SIZE LDBL_MANT_DIG +#else +# error +#endif + +#define REP_BIT ((UDWtype) 1 << (DI_SIZE - DF_SIZE)) + + /* Protect against double-rounding error. + Represent any low-order bits, that might be truncated by a bit that + won't be lost. The bit can go in anywhere below the rounding position + of the SFmode. A fixed mask and bit position handles all usual + configurations. It doesn't handle the case of 128-bit DImode, however. */ + if (DF_SIZE < DI_SIZE + && DF_SIZE > (DI_SIZE - DF_SIZE + SF_SIZE)) + { + if (u >= ((UDWtype) 1 << DF_SIZE)) + { + if ((UDWtype) u & (REP_BIT - 1)) + { + u &= ~ (REP_BIT - 1); + u |= REP_BIT; + } + } + } + + /* Do the calculation in DFmode so that we don't lose any of the + precision of the high word while multiplying it. */ + DFtype f = (UWtype) (u >> W_TYPE_SIZE); + f *= Wtype_MAXp1_F; + f += (UWtype)u; + return (SFtype) f; +#else + /* Finally, the word size is larger than the number of bits in SFmode, + and we've got no DFmode. The only way to avoid double rounding is + to special case the extraction. */ + + /* If there are no high bits set, fall back to one conversion. */ + if ((UWtype)u == u) + return (SFtype)(UWtype)u; + + /* Otherwise, find the power of two. */ + UWtype hi = u >> W_TYPE_SIZE; + + UWtype count, shift; + count_leading_zeros (count, hi); + + shift = W_TYPE_SIZE - count; + + /* Shift down the most significant bits. */ + hi = u >> shift; + + /* If we lost any nonzero bits, set the lsb to ensure correct rounding. */ + if (u & ((1 << shift) - 1)) + hi |= 1; + + /* Convert the one word of data, and rescale. */ + SFtype f = hi; + f *= (UWtype)1 << shift; + return f; +#endif +} +#endif + #if defined(L_fixunsxfsi) && LIBGCC2_HAS_XF_MODE /* Reenable the normal types, in case limits.h needs them. */ #undef char diff --git a/gcc/libgcc2.h b/gcc/libgcc2.h index 56ffc61f4be..11a1d9a3ff8 100644 --- a/gcc/libgcc2.h +++ b/gcc/libgcc2.h @@ -238,6 +238,10 @@ typedef int word_type __attribute__ ((mode (__word__))); #define __floatditf __NDW(float,tf) #define __floatdidf __NDW(float,df) #define __floatdisf __NDW(float,sf) +#define __floatundixf __NDW(floatun,xf) +#define __floatunditf __NDW(floatun,tf) +#define __floatundidf __NDW(floatun,df) +#define __floatundisf __NDW(floatun,sf) #define __fixunsxfSI __NW(fixunsxf,) #define __fixunstfSI __NW(fixunstf,) #define __fixunsdfSI __NW(fixunsdf,) @@ -318,6 +322,7 @@ extern SItype __negvsi2 (SItype); #if LIBGCC2_HAS_SF_MODE extern DWtype __fixsfdi (SFtype); extern SFtype __floatdisf (DWtype); +extern SFtype __floatundisf (UDWtype); extern UWtype __fixunssfSI (SFtype); extern DWtype __fixunssfDI (SFtype); extern SFtype __powisf2 (SFtype, int); @@ -327,6 +332,7 @@ extern SCtype __mulsc3 (SFtype, SFtype, SFtype, SFtype); #if LIBGCC2_HAS_DF_MODE extern DWtype __fixdfdi (DFtype); extern DFtype __floatdidf (DWtype); +extern DFtype __floatundidf (UDWtype); extern UWtype __fixunsdfSI (DFtype); extern DWtype __fixunsdfDI (DFtype); extern DFtype __powidf2 (DFtype, int); @@ -338,6 +344,7 @@ extern DCtype __muldc3 (DFtype, DFtype, DFtype, DFtype); extern DWtype __fixxfdi (XFtype); extern DWtype __fixunsxfDI (XFtype); extern XFtype __floatdixf (DWtype); +extern XFtype __floatundixf (UDWtype); extern UWtype __fixunsxfSI (XFtype); extern XFtype __powixf2 (XFtype, int); extern XCtype __divxc3 (XFtype, XFtype, XFtype, XFtype); @@ -348,6 +355,7 @@ extern XCtype __mulxc3 (XFtype, XFtype, XFtype, XFtype); extern DWtype __fixunstfDI (TFtype); extern DWtype __fixtfdi (TFtype); extern TFtype __floatditf (DWtype); +extern TFtype __floatunditf (UDWtype); extern TFtype __powitf2 (TFtype, int); extern TCtype __divtc3 (TFtype, TFtype, TFtype, TFtype); extern TCtype __multc3 (TFtype, TFtype, TFtype, TFtype); diff --git a/gcc/mklibgcc.in b/gcc/mklibgcc.in index 6517b1d6fce..816e301023b 100644 --- a/gcc/mklibgcc.in +++ b/gcc/mklibgcc.in @@ -63,7 +63,7 @@ lib2funcs='_muldi3 _negdi2 _lshrdi3 _ashldi3 _ashrdi3 _ffssi2 _ffsdi2 _clz _clzsi2 _clzdi2 _ctzsi2 _ctzdi2 _popcount_tab _popcountsi2 _popcountdi2 _paritysi2 _paritydi2 _powisf2 _powidf2 _powixf2 _powitf2 _mulsc3 _muldc3 _mulxc3 _multc3 _divsc3 _divdc3 - _divxc3 _divtc3' + _divxc3 _divtc3 _floatundidf _floatundisf _floatundixf _floatunditf' # Disable SHLIB_LINK if shared libgcc not enabled. if [ "@enable_shared@" = "no" ]; then diff --git a/gcc/optabs.c b/gcc/optabs.c index 6b681a350c1..d764017a5d9 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -4310,6 +4310,7 @@ expand_float (rtx to, rtx from, int unsignedp) enum insn_code icode; rtx target = to; enum machine_mode fmode, imode; + bool can_do_signed = false; /* Crash now, because we won't be able to decide which mode to use. */ gcc_assert (GET_MODE (from) != VOIDmode); @@ -4331,8 +4332,14 @@ expand_float (rtx to, rtx from, int unsignedp) continue; icode = can_float_p (fmode, imode, unsignedp); - if (icode == CODE_FOR_nothing && imode != GET_MODE (from) && unsignedp) - icode = can_float_p (fmode, imode, 0), doing_unsigned = 0; + if (icode == CODE_FOR_nothing && unsignedp) + { + enum insn_code scode = can_float_p (fmode, imode, 0); + if (scode != CODE_FOR_nothing) + can_do_signed = true; + if (imode != GET_MODE (from)) + icode = scode, doing_unsigned = 0; + } if (icode != CODE_FOR_nothing) { @@ -4353,7 +4360,7 @@ expand_float (rtx to, rtx from, int unsignedp) /* Unsigned integer, and no way to convert directly. Convert as signed, then conditionally adjust the result. */ - if (unsignedp) + if (unsignedp && can_do_signed) { rtx label = gen_label_rtx (); rtx temp; @@ -5232,6 +5239,8 @@ init_optabs (void) /* Conversions. */ init_interclass_conv_libfuncs (sfloat_optab, "float", MODE_INT, MODE_FLOAT); + init_interclass_conv_libfuncs (ufloat_optab, "floatun", + MODE_INT, MODE_FLOAT); init_interclass_conv_libfuncs (sfix_optab, "fix", MODE_FLOAT, MODE_INT); init_interclass_conv_libfuncs (ufix_optab, "fixuns", diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 40b1ecaa0b5..c75ea547a70 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2005-11-22 Joseph S. Myers + + * gcc.c-torture/execute/floatunsisf-1.c: New test. + 2005-11-22 Joseph S. Myers * gcc.target/powerpc/405-macchw-1.c, diff --git a/gcc/testsuite/gcc.c-torture/execute/floatunsisf-1.c b/gcc/testsuite/gcc.c-torture/execute/floatunsisf-1.c new file mode 100644 index 00000000000..cc4c49bbe05 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/floatunsisf-1.c @@ -0,0 +1,21 @@ +/* The fp-bit.c function __floatunsisf had a latent bug where guard bits + could be lost leading to incorrect rounding. */ +/* Origin: Joseph Myers */ + +extern void abort (void); +extern void exit (int); +#if __INT_MAX__ >= 0x7fffffff +volatile unsigned u = 0x80000081; +#else +volatile unsigned long u = 0x80000081; +#endif +volatile float f1, f2; +int +main (void) +{ + f1 = (float) u; + f2 = (float) 0x80000081; + if (f1 != f2) + abort (); + exit (0); +} -- cgit v1.2.1