The following test code -- begin -- typedef struct { int val; } Foo; int func(long longPtr) { Foo *pFoo = *(Foo **)&longPtr; /* BAD! */ /* Foo *pFoo = (Foo *)longPtr; If you do this instead it works */ return pFoo->val; }
int main(int argc, const char *argv[]) { Foo foo; foo.val = 1; return func((long)(&foo)); } -- end -- When compiled with -O2 (which implies -fstrict-aliasing) on the x86_64 architectures changes the generated assembly sequence < movl (%rdi), %eax < movq %rdi, -8(%rsp) -- > movq -8(%rsp), %rax > movl (%rax), %eax Comparing to other architectures, for example, i686 - the -O2 generated code with -fstrict-aliasing and -fno-strict-aliasing is identical. The code generated with strict aliasing on the x86_64 is pretty much nonsense and in the case of this test program will result in garbage from the stack being returned from main (or, possibly, a seg fault) Compare x86_64 $ gcc -O2 -Wall badcase.c; ./a.out; echo $? badcase.c: In function func: badcase.c:9: warning: dereferencing type-punned pointer will break strict-aliasing rules 76 $ gcc -O2 -fno-strict-aliasing -Wall badcase.c; ./a.out; echo $? 1 with i686 $ gcc -O2 -Wall badcase.c; ./a.out; echo $? badcase.c: In function `func': badcase.c:9: warning: dereferencing type-punned pointer will break strict-aliasing rules 1 Please note that putting in any diagnostic code into func (for example a print statement) will "fix" it because it changes the determination of aliasing. This makes this particular interaction extra hard to spot. After some considerable debate with my colleagues about the nature of the code and its use of type-punning to convert a long into a pointer, I decided to submit this as a bug because (1) google reveals that a great many distribution builders are adding -fno-strict-aliasing to get their distributions building and working on x86_64 and (2) this ugly construct is the exact type of code that the current version of the (somewhat) popular swig library wrapper generates. I intend to open a bug against SWIG as well as there is no good reason why a code generator should generate code like this. Some of us believe that this code violates the standard (although how specifically they cannot say) and, thus, the compiler is under no obligation to compile it correctly. Even so, in this particular case, it would be better not to generate the eroneous instruction sequence. Hopefully it will be reasonably easy to pin down under which conditions the instruction sequence is changed, for this particular architecture, and perhaps this might point the way to a more fundamental bug. $ gcc -v Using built-in specs. Target: x86_64-redhat-linux Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-libgcj-multifile --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre --with-cpu=generic --host=x86_64-redhat-linux Thread model: posix gcc version 4.1.0 20060304 (Red Hat 4.1.0-3) -- Summary: Type-punned pointer passed as function parameter generates bad assembly sequence Product: gcc Version: 4.1.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c AssignedTo: unassigned at gcc dot gnu dot org ReportedBy: sorenj at us dot ibm dot com GCC build triplet: x86_64-linux-gnu GCC host triplet: x86_64-linux-gnu GCC target triplet: x86_64-linux-gnu http://gcc.gnu.org/bugzilla/show_bug.cgi?id=28073