[1.35][functional] gcc-4.2:dereferencing type-punned pointer will break strict-aliasing rules

Hi, In boost 1.35.0 I'm getting many of the following warnings from gcc-4.2.0 under cygwin when using boost/functional/hash: ../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(float)': ../boost_libraries/boost/functional/detail/hash_float.hpp:75: warning: dereferencing type-punned pointer will break strict-aliasing rules ../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(double)': ../boost_libraries/boost/functional/detail/hash_float.hpp:82: warning: dereferencing type-punned pointer will break strict-aliasing rules ../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(long double)': ../boost_libraries/boost/functional/detail/hash_float.hpp:90: warning: dereferencing type-punned pointer will break strict-aliasing rules Is this a boost bug, or a spurious warning? Is there a fix or a sound workaround? Thanks, Mat

Mat Marcus wrote:
Is this a boost bug, or a spurious warning? Is there a fix or a sound workaround?
I ran into the same problem and use as a work-around: <cxxflags>-fno-strict-aliasing in the options suuplied to gcc in my user-config.jam -- _________________________________________ _ _ | Roland Schwarz |_)(_ | aka. speedsnail | \__) | mailto:roland.schwarz@chello.at ________| http://www.blackspace.at

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Wednesday 16 April 2008 22:22 pm, Mat Marcus wrote:
Hi,
In boost 1.35.0 I'm getting many of the following warnings from gcc-4.2.0 under cygwin when using boost/functional/hash:
../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(float)':
Is this a boost bug, or a spurious warning? Is there a fix or a sound workaround?
It looks like a real bug to me, from glancing at the header. It's modifying a float variable through an integer pointer, which will break badly on gcc with optimization turned on, unless -fno-strict-aliasing is used as Roland suggested. - -- Frank -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFICL085vihyNWuA4URAtyyAJoD2d8S2BbUSXPct22/OA+WFmxNRQCeNa27 rD8TNziy1O020oofjE8pTfI= =8jXe -----END PGP SIGNATURE-----

On Thu, Apr 17, 2008 at 4:22 AM, Mat Marcus <mat-lists@emarcus.org> wrote:
Hi,
In boost 1.35.0 I'm getting many of the following warnings from gcc-4.2.0 under cygwin when using boost/functional/hash:
../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(float)': ../boost_libraries/boost/functional/detail/hash_float.hpp:75: warning: dereferencing type-punned pointer will break strict-aliasing rules ../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(double)': ../boost_libraries/boost/functional/detail/hash_float.hpp:82: warning: dereferencing type-punned pointer will break strict-aliasing rules ../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(long double)': ../boost_libraries/boost/functional/detail/hash_float.hpp:90: warning: dereferencing type-punned pointer will break strict-aliasing rules
Is this a boost bug, or a spurious warning? Is there a fix or a sound workaround?
The warning is correct. GCC can produce wrong code with -fstrict-aliasing (the default). Casting a float* to to uint32_t* and dereferencing the result is UB (if the pointer didn't point to an integer in the first place of course). Changing: inline std::size_t float_hash_impl(float v) { boost::uint32_t* ptr = (boost::uint32_t*)&v; std::size_t seed = *ptr; return seed; } to inline std::size_t float_hash_impl(float v) { std::size_t seed; std::memcpy(&x, &v, sizeof(x)); return seed; } should work and should produce optimal code, at least with a recent gcc. Similar transforms could be applied to float_has_impl([long] double). BTW, is BOOST_HASH_USE_x86_BINARY_HASH only defined for __CYGWIN__ or I'm reading the #ifdefs incorrectly? -- gpd

[mailto:boost-bounces@lists.boost.org] On Behalf Of Giovanni Piero Deretta Sent: Friday, April 18, 2008 8:26 AM To: boost@lists.boost.org Subject: Re: [boost] [1.35][functional] gcc-4.2:dereferencing type-punnedpointer will break strict-aliasing rules
On Thu, Apr 17, 2008 at 4:22 AM, Mat Marcus <mat-lists@emarcus.org> wrote:
Hi,
In boost 1.35.0 I'm getting many of the following warnings from gcc-4.2.0 under cygwin when using boost/functional/hash:
../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(float)':
../boost_libraries/boost/functional/detail/hash_float.hpp:75: warning:
dereferencing type-punned pointer will break strict-aliasing rules ../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(double)':
../boost_libraries/boost/functional/detail/hash_float.hpp:82: warning:
dereferencing type-punned pointer will break strict-aliasing rules ../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(long double)':
../boost_libraries/boost/functional/detail/hash_float.hpp:90: warning:
dereferencing type-punned pointer will break strict-aliasing rules
Is this a boost bug, or a spurious warning? Is there a fix or a sound workaround?
The warning is correct. GCC can produce wrong code with -fstrict-aliasing (the default). Casting a float* to to uint32_t* and dereferencing the result is UB (if the pointer didn't point to an integer in the first place of course).
Changing:
inline std::size_t float_hash_impl(float v) { boost::uint32_t* ptr = (boost::uint32_t*)&v; std::size_t seed = *ptr; return seed; }
to
inline std::size_t float_hash_impl(float v) { std::size_t seed; std::memcpy(&x, &v, sizeof(x)); return seed; }
/x/seed/ ??

On Fri, Apr 18, 2008 at 5:34 PM, Matt Doyle <mdoyle@a-m-c.com> wrote:
[mailto:boost-bounces@lists.boost.org] On Behalf Of Giovanni Piero Deretta Sent: Friday, April 18, 2008 8:26 AM To: boost@lists.boost.org Subject: Re: [boost] [1.35][functional] gcc-4.2:dereferencing type-punnedpointer will break strict-aliasing rules
On Thu, Apr 17, 2008 at 4:22 AM, Mat Marcus <mat-lists@emarcus.org> wrote:
Hi,
In boost 1.35.0 I'm getting many of the following warnings from gcc-4.2.0 under cygwin when using boost/functional/hash:
../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(float)':
../boost_libraries/boost/functional/detail/hash_float.hpp:75: warning:
dereferencing type-punned pointer will break strict-aliasing rules ../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(double)':
../boost_libraries/boost/functional/detail/hash_float.hpp:82: warning:
dereferencing type-punned pointer will break strict-aliasing rules ../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(long double)':
../boost_libraries/boost/functional/detail/hash_float.hpp:90: warning:
dereferencing type-punned pointer will break strict-aliasing rules
Is this a boost bug, or a spurious warning? Is there a fix or a sound workaround?
The warning is correct. GCC can produce wrong code with -fstrict-aliasing (the default). Casting a float* to to uint32_t* and dereferencing the result is UB (if the pointer didn't point to an integer in the first place of course).
Changing:
inline std::size_t float_hash_impl(float v) { boost::uint32_t* ptr = (boost::uint32_t*)&v; std::size_t seed = *ptr; return seed; }
to
inline std::size_t float_hash_impl(float v) { std::size_t seed; std::memcpy(&x, &v, sizeof(x)); return seed; }
/x/seed/ ??
Yes, of course :) -- gpd

Giovanni Piero Deretta wrote:
On Fri, Apr 18, 2008 at 5:34 PM, Matt Doyle <mdoyle@a-m-c.com> wrote:
[mailto:boost-bounces@lists.boost.org] On Behalf Of Giovanni Piero Deretta Sent: Friday, April 18, 2008 8:26 AM To: boost@lists.boost.org Subject: Re: [boost] [1.35][functional] gcc-4.2:dereferencing type-punnedpointer will break strict-aliasing rules
On Thu, Apr 17, 2008 at 4:22 AM, Mat Marcus <mat-lists@emarcus.org> wrote:
Hi,
In boost 1.35.0 I'm getting many of the following warnings
from > > gcc-4.2.0 under cygwin when using boost/functional/hash:
../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(float)':
../boost_libraries/boost/functional/detail/hash_float.hpp:75: warning: > > dereferencing type-punned pointer will break strict-aliasing rules > > ../boost_libraries/boost/functional/detail/hash_float.hpp: In > > function 'size_t boost::hash_detail::float_hash_impl(double)': >
../boost_libraries/boost/functional/detail/hash_float.hpp:82: warning: > > dereferencing type-punned pointer will break strict-aliasing rules > > ../boost_libraries/boost/functional/detail/hash_float.hpp: In > > function 'size_t boost::hash_detail::float_hash_impl(long double)': > > > ../boost_libraries/boost/functional/detail/hash_float.hpp:90: warning: > > dereferencing type-punned pointer will break strict-aliasing rules > > > > Is this a boost bug, or a spurious warning? Is there a fix > or a sound workaround?
The warning is correct. GCC can produce wrong code with -fstrict-aliasing (the default). Casting a float* to to uint32_t* and dereferencing the result is UB (if the pointer didn't point to an integer in the first place of course).
Changing:
inline std::size_t float_hash_impl(float v) { boost::uint32_t* ptr = (boost::uint32_t*)&v; std::size_t seed = *ptr; return seed; }
to
inline std::size_t float_hash_impl(float v) { std::size_t seed; std::memcpy(&x, &v, sizeof(x)); return seed; }
/x/seed/ ??
Yes, of course :)
And also assuming that sizeof(seed) ==sizeof(float) ? Bo Persson

On Sat, Apr 19, 2008 at 5:30 PM, Bo Persson <bop@gmb.dk> wrote:
Giovanni Piero Deretta wrote:
On Fri, Apr 18, 2008 at 5:34 PM, Matt Doyle <mdoyle@a-m-c.com> wrote:
[mailto:boost-bounces@lists.boost.org] On Behalf Of Giovanni Piero Deretta Sent: Friday, April 18, 2008 8:26 AM To: boost@lists.boost.org Subject: Re: [boost] [1.35][functional] gcc-4.2:dereferencing type-punnedpointer will break strict-aliasing rules
On Thu, Apr 17, 2008 at 4:22 AM, Mat Marcus <mat-lists@emarcus.org> wrote:
Hi,
In boost 1.35.0 I'm getting many of the following warnings
from > > gcc-4.2.0 under cygwin when using boost/functional/hash:
../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(float)':
../boost_libraries/boost/functional/detail/hash_float.hpp:75: warning: > > dereferencing type-punned pointer will break strict-aliasing rules > > ../boost_libraries/boost/functional/detail/hash_float.hpp: In > > function 'size_t boost::hash_detail::float_hash_impl(double)': >
../boost_libraries/boost/functional/detail/hash_float.hpp:82: warning: > > dereferencing type-punned pointer will break strict-aliasing rules > > ../boost_libraries/boost/functional/detail/hash_float.hpp: In > > function 'size_t boost::hash_detail::float_hash_impl(long double)': > > > ../boost_libraries/boost/functional/detail/hash_float.hpp:90: warning: > > dereferencing type-punned pointer will break strict-aliasing rules > > > > Is this a boost bug, or a spurious warning? Is there a fix > or a sound workaround?
The warning is correct. GCC can produce wrong code with -fstrict-aliasing (the default). Casting a float* to to uint32_t* and dereferencing the result is UB (if the pointer didn't point to an integer in the first place of course).
Changing:
inline std::size_t float_hash_impl(float v) { boost::uint32_t* ptr = (boost::uint32_t*)&v; std::size_t seed = *ptr; return seed; }
to
inline std::size_t float_hash_impl(float v) { std::size_t seed; std::memcpy(&x, &v, sizeof(x)); return seed; }
/x/seed/ ??
Yes, of course :)
And also assuming that sizeof(seed) ==sizeof(float) ?
Yes, but, as the code is only compiled in when under X86, you can safely assume that. -- gpd

Giovanni Piero Deretta wrote:
On Sat, Apr 19, 2008 at 5:30 PM, Bo Persson <bop@gmb.dk> wrote:
Giovanni Piero Deretta wrote:
On Fri, Apr 18, 2008 at 5:34 PM, Matt Doyle <mdoyle@a-m-c.com> wrote:
[mailto:boost-bounces@lists.boost.org] On Behalf Of Giovanni Piero Deretta
Changing:
inline std::size_t float_hash_impl(float v) { boost::uint32_t* ptr = (boost::uint32_t*)&v; std::size_t seed = *ptr; return seed; }
to
inline std::size_t float_hash_impl(float v) { std::size_t seed; std::memcpy(&x, &v, sizeof(x)); return seed; }
/x/seed/ ??
Yes, of course :)
And also assuming that sizeof(seed) ==sizeof(float) ?
Yes, but, as the code is only compiled in when under X86, you can safely assume that.
Ok, I though it was used for x86_64 as well, but I might be wrong. Bo Persson

On Sat, Apr 19, 2008 at 8:38 PM, Bo Persson <bop@gmb.dk> wrote:
Giovanni Piero Deretta wrote:
On Sat, Apr 19, 2008 at 5:30 PM, Bo Persson <bop@gmb.dk> wrote:
Giovanni Piero Deretta wrote:
On Fri, Apr 18, 2008 at 5:34 PM, Matt Doyle <mdoyle@a-m-c.com> wrote:
[mailto:boost-bounces@lists.boost.org] On Behalf Of Giovanni Piero Deretta
Changing:
inline std::size_t float_hash_impl(float v) { boost::uint32_t* ptr = (boost::uint32_t*)&v; std::size_t seed = *ptr; return seed; }
to
inline std::size_t float_hash_impl(float v) { std::size_t seed; std::memcpy(&x, &v, sizeof(x)); return seed; }
/x/seed/ ??
Yes, of course :)
And also assuming that sizeof(seed) ==sizeof(float) ?
Yes, but, as the code is only compiled in when under X86, you can safely assume that.
Ok, I though it was used for x86_64 as well, but I might be wrong.
If I have read the preprocessor conditionals correctly, oddly enough it will enabled only on cygwin/x86. In theory the same code should work for all gccs (it should work for all x86 compilers in fact). And yes, it will break for x86_64 cpus, where size_t is 64 bits, but that is easily fixed. -- gpd

On Fri, Apr 18, 2008 at 8:25 AM, Giovanni Piero Deretta <gpderetta@gmail.com> wrote:
On Thu, Apr 17, 2008 at 4:22 AM, Mat Marcus <mat-lists@emarcus.org> wrote:
Hi,
In boost 1.35.0 I'm getting many of the following warnings from gcc-4.2.0 under cygwin when using boost/functional/hash:
../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(float)': ../boost_libraries/boost/functional/detail/hash_float.hpp:75: warning: dereferencing type-punned pointer will break strict-aliasing rules ../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(double)': ../boost_libraries/boost/functional/detail/hash_float.hpp:82: warning: dereferencing type-punned pointer will break strict-aliasing rules ../boost_libraries/boost/functional/detail/hash_float.hpp: In function 'size_t boost::hash_detail::float_hash_impl(long double)': ../boost_libraries/boost/functional/detail/hash_float.hpp:90: warning: dereferencing type-punned pointer will break strict-aliasing rules
Is this a boost bug, or a spurious warning? Is there a fix or a sound workaround?
The warning is correct. GCC can produce wrong code with -fstrict-aliasing (the default). Casting a float* to to uint32_t* and dereferencing the result is UB (if the pointer didn't point to an integer in the first place of course).
Changing:
inline std::size_t float_hash_impl(float v) { boost::uint32_t* ptr = (boost::uint32_t*)&v; std::size_t seed = *ptr; return seed; }
to
inline std::size_t float_hash_impl(float v) { std::size_t seed; std::memcpy(&x, &v, sizeof(x)); return seed; }
should work and should produce optimal code, at least with a recent gcc. Similar transforms could be applied to float_has_impl([long] double). BTW, is BOOST_HASH_USE_x86_BINARY_HASH only defined for __CYGWIN__ or I'm reading the #ifdefs incorrectly?
-- gpd
Thanks for the explanation and the patch. - Mat

On Friday 18 April 2008, Giovanni Piero Deretta wrote:
inline std::size_t float_hash_impl(float v) { std::size_t seed; std::memcpy(&x, &v, sizeof(x)); return seed; }
should work and should produce optimal code, at least with a recent gcc.
Wouldn't casting using union be safe and efficient in all cases? Something like: inline std::size_t float_hash_impl(float v) { union { float v; std::size_t seed; } u; u.v = v; return u.seed; } Teemu

Teemu Torma wrote:
Wouldn't casting using union be safe and efficient in all cases?
I don't know anything about the specific problem at hand, but this hopefully is in carefully controlled code, i.e. platform dependant and the like. AFAIK this usage is not std c++. (But I might be wrong.) -- _________________________________________ _ _ | Roland Schwarz |_)(_ | aka. speedsnail | \__) | mailto:roland.schwarz@chello.at ________| http://www.blackspace.at

[mailto:boost-bounces@lists.boost.org] On Behalf Of Roland Schwarz Teemu Torma wrote:
Wouldn't casting using union be safe and efficient in all cases?
I don't know anything about the specific problem at hand, but this hopefully is in carefully controlled code, i.e. platform dependant and the like. AFAIK this usage is not std c++. (But I might be wrong.)
No I think your right. Reading a member of a union other than the last one written to is undefined behavior. See #97 in Sutter-2005 or #C.8.2 in Stroustrup-2000, they both advise strongly against trying this.

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Friday 18 April 2008 17:45 pm, Teemu Torma wrote:
Wouldn't casting using union be safe and efficient in all cases? Something like:
inline std::size_t float_hash_impl(float v) { union { float v; std::size_t seed; } u; u.v = v; return u.seed; }
It's safe with gcc, but that is a gnu extension. The standard only allows aliasing through char*. - -- Frank -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFICSjl5vihyNWuA4URAg0BAKDXUcDHivJCccQFhfkG73zW/1jAvACgr7j6 wobgG+feqehkUEnxAhkau7k= =A3yE -----END PGP SIGNATURE-----

On Fri, Apr 18, 2008 at 11:45 PM, Teemu Torma <teemu@torma.org> wrote:
On Friday 18 April 2008, Giovanni Piero Deretta wrote:
inline std::size_t float_hash_impl(float v) { std::size_t seed; std::memcpy(&x, &v, sizeof(x)); return seed; }
should work and should produce optimal code, at least with a recent gcc.
Wouldn't casting using union be safe and efficient in all cases? Something like:
inline std::size_t float_hash_impl(float v) { union { float v; std::size_t seed; } u; u.v = v; return u.seed; }
This is UB according to the standard, but it happens to works in most compilers (gcc explicitly states that this is guaranteed to work), but: - memcpy produces already optimal code at least with gcc. - gcc sometimes is more conservative with optimizations when it sees an union (in fact I've seen gcc produce worse code with the union than with the memcpy). HTH, -- gpd
participants (7)
-
Bo Persson
-
Frank Mori Hess
-
Giovanni Piero Deretta
-
Mat Marcus
-
Matt Doyle
-
Roland Schwarz
-
Teemu Torma