This is the mail archive of the
gcc-help@gcc.gnu.org
mailing list for the GCC project.
Re: strict aliasing rule and examples
- From: Andrew Haley <aph at redhat dot com>
- To: Mojmir Svoboda <mojmir dot svoboda at illusionsoftworks dot com>
- Cc: gcc-help at gcc dot gnu dot org
- Date: Tue, 26 Feb 2008 17:43:53 +0000
- Subject: Re: strict aliasing rule and examples
- References: <20080226145845.GA31582@msvoboda>
Mojmir Svoboda wrote:
hello,
i've just encountered some code breaking the aliasing rule:
#include <cstdio>
unsigned EndianSwap (unsigned data) {
return (data << 24 & 0xff000000) | (data << 8 & 0x00ff0000)
| (data >> 8 & 0x0000ff00) | (data >> 24 & 0x000000ff);
}
float EndianSwap (float data) {
unsigned res = EndianSwap(*reinterpret_cast<const unsigned *>(&data));
return *reinterpret_cast<float*>(&res);
}
int main () {
float aa = 123.456f;
float bb = EndianSwap(aa);
printf("swap: %f -> %f\n", aa, bb);
}
the thing is that i can see what happens if -O3 and -fstrict-aliasing
is on, but i'd like to know what's going on under-the-hood (if it is
almost-human explainable):
If you are to understand this at all, you *must* read the appropriate
section of the ISO C standard. This is Section 6.3.2.3. Search for
iso-9899 on the web. This section tells you which pointer conversions are
defined, and any pointer conversion not defined in that section has no
meaning.
i mean - what is compiler doing to optimize the whole swap code out?
The code is meaningless, so the compiler doesn't have to do anything
at all. What actually happens is that the compiler can see that the
statements have no effect, so the whole lot gets dropped on the floor.
and yet another (perhaps related) thing: let's have following code:
struct vect {
float x, y, z;
float & operator[] (int i) { return *(&x + i); }
};
i know that there is a potential hazard with padding, but
gcc guru of mine told me that this is in fact breaking of the aliasing
rule too, but after few beers i stopped following his arguments.
is it? how does it break the rule?
It's not one of the well-defined conversions in Section 6.3.2.3.
Basically, the rule is this: you cannot take the address of an object
and through pointer type conversion create an lvalue of an incompatible
type.
gcc has a useful extension, which is supported my many compilers:
float EndianSwap (float data) {
union
{
float fd;
unsigned ud;
} u;
u.fd = data;
u.ud = EndianSwap(u.ud);
return u.fd;
}
This is much nicer anyway, and it much more clearly describes what you're
trying to do.
Andrew.