Bug 38297 - O2 causes invalid code
O2 causes invalid code
Status: RESOLVED DUPLICATE of bug 21920
Product: gcc
Classification: Unclassified
Component: c++
4.3.2
: P3 critical
: ---
Assigned To: Not yet assigned to anyone
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2008-11-28 00:28 UTC by WARnux
Modified: 2008-11-30 11:43 UTC (History)
27 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description WARnux 2008-11-28 00:28:48 UTC
I am compiling with g++ on a 64 bit Ubuntu OS.  I spent days trying to find out why my programs kept crashing.  I found that when using O2 optimizations the problem is there, when using O1 it is not a problem.

I am not using any compiler options other than stripping symbols, hiding warnings, and O2.

Here is a very simple test case that causes the bug every time.

void RefTest(byte*& p)
{
	++p;
}
void RefTest(char*& p)
{
	++p;
}

char* c= "0123";
char* p= c;
RefTest((byte*&)p);
cout << p << endl; // 0123
RefTest(p);
cout << p << endl; // 123

return 0;
Comment 1 WARnux 2008-11-28 00:32:03 UTC
I guess you want this too:

gcc -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.3.2-1ubuntu11' --with-bugurl=file:///usr/share/doc/gcc-4.3/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.3 --program-suffix=-4.3 --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-mpfr --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.3.2 (Ubuntu 4.3.2-1ubuntu11)
Comment 2 Andrew Pinski 2008-11-28 01:38:21 UTC
What is byte typedef?  Is it unsigned char or signed char?  If so then you are violating aliasing rules by modifying a char* via that pointer type.  Yes char/unsigned char/signed char are special but their pointer types are not.
Comment 3 WARnux 2008-11-28 01:59:18 UTC
unsigned char

char and byte are the same size (1 byte), so why is there a problem?
Comment 4 Andrew Pinski 2008-11-28 02:20:58 UTC
Yep you are violating C++ aliasing rules as you are accessing a char * as an unsigned char*.  It would be ok if you accessed a char as an unsigned char but you are accessing the pointers instead.

It is not the size which matters but rather the types which are being accessed here.

*** This bug has been marked as a duplicate of 21920 ***
Comment 5 WARnux 2008-11-28 02:33:39 UTC
Thanks!  I guess I learned something new today.
Comment 6 WARnux 2008-11-28 16:38:44 UTC
I do have a couple concerns:

1. This one is MAJOR.  Without using -Wstrict-aliasing, I never see warnings about this change.  I can't even begin to explain how bad that is.  The gcc programmers made a big change (that is default compile) and there isn't even a warning by default!  I wonder how many other things like this have been done.

2. Will other compilers compile this kind of code correctly?
inline bool IsNegative(float f)
{
    union
    {
        float f;
        int i;
    } u;
    u.f = f;
    return u.i & 0x80000000;
}
http://xania.org/200712/cpp-strict-aliasing
Comment 7 Andrew Pinski 2008-11-28 16:52:56 UTC
1) Yes that is the reason why -Wstrict-aliasing exist.  This is undefined behavior at runtime not at compile time so we cannot error out.  It is also the reason why -Wstrict-aliasing is enabled with -Wall.

2) Use memcpy instead of an union if you want a portable way to type pun.

*** This bug has been marked as a duplicate of 21920 ***
Comment 8 cdfrey 2008-11-28 19:24:43 UTC
Why is the union access not portable, and listed as a GCC extension?

According to the quotation of the standard at:
http://mail-index.netbsd.org/tech-kern/2003/08/11/0001.html
(this link is found in the GCC docs on this topic)

The standard seems to list a union as a valid way to do type punning.

I can understand if it's just a matter of other compilers being buggy, but is there any other reason that might make this non-portable?

What am I missing?

Thanks,
- Chris
Comment 9 WARnux 2008-11-28 22:01:41 UTC
I have another question.  I want to be able to detect if fno-strict-aliasing was used when compiling.  Preferably at compile time but run time will be fine.  How can I do this?
Comment 10 WARnux 2008-11-28 22:02:50 UTC
The shortest answer possible will be fine.  I don't want to be an annoyance.
Comment 11 Andrew Pinski 2008-11-28 23:32:15 UTC
There is no way currently inside the code to figure out if the C/C++ aliasing rules are activated or not.  And I hope there will never be a way because it is better to fix up your code.  The reason why the union case is considered unspecified is because it depends on the under laying bit representation of float. 

*** This bug has been marked as a duplicate of 21920 ***
Comment 12 cdfrey 2008-11-28 23:36:06 UTC
> The reason why the union case is considered
> unspecified is because it depends on the under
> laying bit representation of float.

That makes sense.  In this case, it's not really a type punning issue and more of a float issue.  We'd be up the same creek without a paddle if we used memcpy() on floats too.

Thanks,
- Chris
Comment 13 Richard Biener 2008-11-30 11:43:04 UTC
Note that the C standard forbids type-punning through a union.  Basically it says
that you may only read from a union member if you have previously written to it.
It also says that all other bits apart from the ones you have written to contain
undefined values after the write.  So

 union { int i; float f; } u;
 u.i = 1;
 x = u.f;

invokes undefined behavior in C (but not in GNU C because of the language
extension).
Comment 14 joseph@codesourcery.com 2008-11-30 15:37:17 UTC
Subject: Re:  O2 causes invalid code

On Sun, 30 Nov 2008, rguenth at gcc dot gnu dot org wrote:

> Note that the C standard forbids type-punning through a union.  
> Basically it says that you may only read from a union member if you have 
> previously written to it. It also says that all other bits apart from 
> the ones you have written to contain undefined values after the write.  
> So
> 
>  union { int i; float f; } u;
>  u.i = 1;
>  x = u.f;
> 
> invokes undefined behavior in C (but not in GNU C because of the language
> extension).

Note that C99 TC3 adds a footnote: "*) If the member used to access the 
contents of a union object is not the same as the member last used to 
store a value in the object, the appropriate part of the object 
representation of the value is reinterpreted as an object representation 
in the new type as described in 6.2.6 (a process sometimes called "type 
punning"). This might be a trap representation."