strict aliasing: cast from char[] or char *
Hi, Sorry, if it has been discussed before, I found a lot of information on strict aliasing in gcc, but nothing about this particular case. I'd like to code a custom container class: it has a char[] (or dynamically allocated "char *") for storage, putting an element is done with placement new, getting an element is done with reinterpret_cast'ing the storage, like this: #include struct Something { int value; Something() { value = 42; } }; template struct Container { union { char data[sizeof(OBJECT)*MAX_SIZE]; // here goes a member which ensures proper alignment }; int currentSize; Container() { currentSize=0; } void add() { new(data+sizeof(OBJECT)*currentSize) OBJECT; currentSize++; } OBJECT &operator[](int index) { // assert(index(data)[index]; // gcc warns here } }; void bar() { Container c; c.add(); c[0].value = 41; } Here, gcc warns on the reinterpret_cast line. However, as I understand, this usage cannot cause any harm. If I always access "data" as "OBJECT", nothing can go wrong, right? Even, if I access "data" as char sometimes, gcc should handle this case, I think. Of course, if "data" would be a non-char array, the situation would be different. Is the warning appropriate here? Can a cast from char[] cause anything bad? If it's difficult to detect this usage method on the compiler side, is there any way to avoid this warning just for this line (maybe rephrasing this line somehow to tell gcc that everything is OK)? Thanks, Geza
Re: strict aliasing: cast from char[] or char *
On 06/07/2011 12:27 PM, Richard Guenther wrote: On Tue, Jun 7, 2011 at 5:51 AM, Herman, Geza wrote: Hi, Sorry, if it has been discussed before, I found a lot of information on strict aliasing in gcc, but nothing about this particular case. I'd like to code a custom container class: it has a char[] (or dynamically allocated "char *") for storage, putting an element is done with placement new, getting an element is done with reinterpret_cast'ing the storage, like this: #include struct Something { int value; Something() { value = 42; } }; template struct Container { union { char data[sizeof(OBJECT)*MAX_SIZE]; // here goes a member which ensures proper alignment }; int currentSize; Container() { currentSize=0; } void add() { new(data+sizeof(OBJECT)*currentSize) OBJECT; currentSize++; } OBJECT&operator[](int index) { // assert(index(data)[index]; // gcc warns here } }; void bar() { Container c; c.add(); c[0].value = 41; } Here, gcc warns on the reinterpret_cast line. However, as I understand, this usage cannot cause any harm. If I always access "data" as "OBJECT", nothing can go wrong, right? Even, if I access "data" as char sometimes, gcc should handle this case, I think. Of course, if "data" would be a non-char array, the situation would be different. Is the warning appropriate here? Can a cast from char[] cause anything bad? If it's difficult to detect this usage method on the compiler side, is there any way to avoid this warning just for this line (maybe rephrasing this line somehow to tell gcc that everything is OK)? The code looks ok and should work fine with GCC 4.5 and newer. No guarantees for older versions though, if it works there it certainly isn't by design. Thanks for the answer! You're right, this example compiles without warnings with GCC 4.5. My mistake, I copied a version which warns only with GCC 4.4 in my previous email. But, if I add another member function, like: OBJECT &first() { return reinterpret_cast(data)[0]; // gcc warns here } and call it from "bar()": c.first().value = 41; I tried both GCC 4.5, and the latest available in my system (gcc version 4.7.0 20110531 (experimental) [trunk revision 174470]), both produces a warning. Is it a false positive warning? Luckily, generated code works, even with previous versions of GCC. Richard. Thanks, Geza
Re: strict aliasing: cast from char[] or char *
On 06/07/2011 03:02 PM, Richard Guenther wrote: On Tue, Jun 7, 2011 at 2:58 PM, Herman, Geza wrote: On 06/07/2011 12:27 PM, Richard Guenther wrote: On Tue, Jun 7, 2011 at 5:51 AM, Herman, Gezawrote: Hi, Sorry, if it has been discussed before, I found a lot of information on strict aliasing in gcc, but nothing about this particular case. I'd like to code a custom container class: it has a char[] (or dynamically allocated "char *") for storage, putting an element is done with placement new, getting an element is done with reinterpret_cast'ing the storage, like this: #include struct Something { int value; Something() { value = 42; } }; template struct Container { union { char data[sizeof(OBJECT)*MAX_SIZE]; // here goes a member which ensures proper alignment }; int currentSize; Container() { currentSize=0; } void add() { new(data+sizeof(OBJECT)*currentSize) OBJECT; currentSize++; } OBJECT&operator[](int index) { // assert(index(data)[index]; // gcc warns here } }; void bar() { Containerc; c.add(); c[0].value = 41; } Here, gcc warns on the reinterpret_cast line. However, as I understand, this usage cannot cause any harm. If I always access "data" as "OBJECT", nothing can go wrong, right? Even, if I access "data" as char sometimes, gcc should handle this case, I think. Of course, if "data" would be a non-char array, the situation would be different. Is the warning appropriate here? Can a cast from char[] cause anything bad? If it's difficult to detect this usage method on the compiler side, is there any way to avoid this warning just for this line (maybe rephrasing this line somehow to tell gcc that everything is OK)? The code looks ok and should work fine with GCC 4.5 and newer. No guarantees for older versions though, if it works there it certainly isn't by design. Thanks for the answer! You're right, this example compiles without warnings with GCC 4.5. My mistake, I copied a version which warns only with GCC 4.4 in my previous email. But, if I add another member function, like: OBJECT&first() { return reinterpret_cast(data)[0]; // gcc warns here } and call it from "bar()": c.first().value = 41; I tried both GCC 4.5, and the latest available in my system (gcc version 4.7.0 20110531 (experimental) [trunk revision 174470]), both produces a warning. Is it a false positive warning? Yes. Okay :) Here's my last question (I hope). As GCC cannot know the usage pattern of char[], it's good that it emits a warning. For example, if we'd remove the warning (just for the case where char[] is casted), this case wouldn't get a warning, and would generate incorrect code: char data[...]; reinterpret_cast(data) = 1; printf("%d\n", reinterpret_cast(data)); However, for my construct, which appears to be completely legal, I get a warning, which I'd like to disable. How can I do that? Currently I'm using -Wno-strict-aliasing, but I'd like to have a better solution. I tried to cast (void*) before the cast to (OBJECT*), it didn't help. Is it possible to disable this warning for this line only (maybe with some GCC specific tricks)? Thanks, Geza Richard. Luckily, generated code works, even with previous versions of GCC. Richard. Thanks, Geza
Type-punning
Hi, gcc's docs states that at -fstrict-aliasing: "In particular, an object of one type is assumed never to reside at the same address as an object of a different type, unless the types are almost the same." I have problems with this: struct A { float x, y; }; struct B { float x, y; }; int main() { A a; B &b = reinterpret_cast(a); } I get a type-punned warning for this code. However, A & B is exactly the same type. Is the warning appropriate here? Where can I find the definition of "almost the same [type]"? A little more complicated example: struct A { float x, y; }; struct B: public A { }; struct C: public A { }; int main() { B b; C &c = reinterpret_cast(b); } I get the same warning, and I even get miscompiled code with -O6 (for a more complicated code, not for this). What is the correct way to do this: void setNaN(float &v) { reinterpret_cast(v) = 0x7f81; } without a type-prunning warning? I cannot use the union trick here (memcpy works though, but it's not the most efficient solution, I suppose). Thanks for your help, Geza PS: gcc-4.1, gcc-4.2 produces this. Earlier gcc versions don't produce warnings for these cases.
Re: Type-punning
Thanks for the answers! > > Why? Won't the following work? > > > > void setNaN(float &v) { > > union { float f; int i; } t; > > t.i = 0x7f81; > > v = t.f; > > } > > > As far as I know, this is guaranteed to work with GCC. But it is not kosher > according to language standards, so other compilers might dislike it. On the > other hand, other compilers are not guaranteed to optimize the call to > "memcpy" out either. Thinking about this setNaN trick, why is it not kosher? It seems completely OK to me (too bad that I missed this "trick"). Note that my problem is not the unnecessary type-punning warning (I could live with that), but miscompiled code because of the too strict rule that GCC uses. Maybe GCC shouldn't optimize around invalid type-punnings? Geza
Re: Type-punning
On Tue, 26 Jun 2007, Andrew Pinski wrote: > On 6/26/07, Herman Geza <[EMAIL PROTECTED]> wrote: > > Maybe GCC shouldn't optimize around invalid type-punnings? > > That is what -fno-strict-aliasing is for. > Also GCC has done this since 3.0.0 (and also 2.95 and 2.95.1 and then > in 2.95.2 it was changed back while most of the free source world > fixes their code). Let me elaborate. If the compiler can detect invalid type-punning, then shouldn't optimize. For example, void foo(int *a, float *b) { // here, access to a and b can be optimized, as the compiler supposedly doesn't know whether a and b point to the same address } but: void foo(float *a) { int *b = (int*)a; // type-punning warning // here, access to a and b shouldn't be optimized, as the compiler knows that a and b point to the same address } Is this reasonable? Geza
Re: Type-punning
On Tue, 26 Jun 2007, Silvius Rus wrote: > Herman Geza wrote: > > void foo(float *a) { > > int *b = (int*)a; // type-punning warning > > > > // here, access to a and b shouldn't be optimized, as the compiler > > knows that a and b point to the same address > > } > > > > Is this reasonable? > > > Even if it were trivial to implement, I would vote against it, because it > would encourage people to write non-compliant code. Agreed, if it results in non-compilant code. I have problems with aliasing when GCC (I think) incorrectly treats types different, but they're the same. For example, I have: struct Point { float x, y, z; }; struct Vector { float x, y, z; Point &asPoint() { return reinterpret_cast(*this); } }; Point and Vector have the same layout, but GCC treats them different when it does aliasing analysis. I have problems when I use Vector::asPoint. I use asPoint very trivially, it is very easy to detect (for a human) that references point to the same address. Like void doSomething(Point p) { // do something with p } void fv() { Point a, b; // initialize a & b doSomething((a-b).asPoint()); // a-b results in a Vector } Here, there's a bad compilation (I think) at the call of doSomething. I solved my problem with struct Vector { Point toPoint() { return Point(x, y, z); } }; GCC optimizes this as well, but I use other compilers which don't. It is not a big issue, I just wanted to know some background behind the warning :) Thanks for your responses. Geza
Re: Type-punning
On Tue, 26 Jun 2007, Silvius Rus wrote: > Herman Geza wrote: > > aliasing when GCC (I think) incorrectly treats types different, but they're > > the same. For example, I have: > > > > struct Point { > > float x, y, z; > > }; > > > > struct Vector { > > float x, y, z; > > > > Point &asPoint() { > > return reinterpret_cast(*this); > > } > > }; > > > > Point and Vector have the same layout, but GCC treats them different when it > > does aliasing analysis. I have problems when I use Vector::asPoint. > I also think this case should not be flagged. I have seen similar usage in > network programming. Did it actually result in bad code or was it just the > warning that bothered you? Bad code. I thought this warning almost always means bad compiled code if the casted object isn't casted back to its original type. However, I've created a simple example, which does almost the same as the badly compiled sourcecode. But this simple example compiled fine. I'm sure that the badly compiled code is fine (I mean there is no programmer error, except the type-punned warning), because: - valgrind spotted only this (and the code doesn't behave as it should) - if I change asPoint to toPoint (which returns a new Point object, not a reinterpret_cast'd reference to *this), valgrind doesn't complain, and the code runs fine. I'll try to make a simple example on which GCC produces bad code. Geza
Re: Type-punning
On Tue, 26 Jun 2007 [EMAIL PROTECTED] wrote: > On Tue, Jun 26, 2007 at 11:42:27PM +0200, Herman Geza wrote: > > struct Point { > > float x, y, z; > > }; > > struct Vector { > > float x, y, z; > > > > Point &asPoint() { > > return reinterpret_cast(*this); > > } > > }; > > > Point and Vector have the same layout, but GCC treats them different when > > it does aliasing analysis. I have problems when I use Vector::asPoint. > > I use asPoint very trivially, it is very easy to detect (for a human) > > that references point to the same address. Like > > As a "human", I don't see how they are the same. Other than having three > fields with the same type, and same name, what makes them the same? Having the same layout makes them "the same". > What happens when Point or Vector have a fourth field added? What if somebody > decides to re-order the fields to "float z, x, y;"? Would you expect the > optimization to be silently disabled? Or would you expect it to guess based > on the variables names? Point and Vector "designed" to have the same layout and nobody is allowed to change this. It worked until now :) Now, it seems a bad design, as the standard says at 3.10.15 (incomplete summary by me): a stored value can be accessed only with an lvalue of the dynamic type of the stored object. They justify this with aliasing. So a Vector shouldn't be accessed via a Point. Undefined behavior happens if someone access an object via a different type (unless it's char or unsigned char, which are allowed); same layout doesn't matter, as Andrew P. said. The compiler is allowed to generate bad code in this case, I think. Geza
Re: Type-punning
On Tue, 26 Jun 2007, Andrew Pinski wrote: > On 6/26/07, Herman Geza <[EMAIL PROTECTED]> wrote: > > Having the same layout makes them "the same". > Not in C or C++ and in most cases of fortran too (unless you add an > attribute). Yes, it's clear now, thanks. However, it brings a new question: the standard defines layout-compatible types. For example, if I'm correct, my Vector and Point are layout compatible. What can I do with layout compatible objects? I found the definiton of layout compatible types, but found no uses of them. Why is it "important" that Point and Vector are layout-compatible? (is this question offtopic here?). Geza
Re: Type-punning
> I'll try to make a simple example on which GCC produces bad code. You can find an example below. GCC-4.1 generates bad code (GCC-4.2 is fine). Neither version give a type-punning warning, though. I'm sorry that I didn't check gcc-4.2 before I started this thread. Geza Here's the code, I couldn't simplify it further to still have bad code with gcc-4.1. gcc version: 4.1.3 20070601 (prerelease) (Debian 4.1.2-12) #include class Tuple { public: union { int c[2]; struct { int x, y; }; }; public: Tuple() { } Tuple(int x, int y) : x(x), y(y) { } }; class Vector; class Point: public Tuple { public: Point() { } Point(int x, int y) : Tuple(x, y) { } inline Vector operator-(const Point &other) const; }; class Vector: public Tuple { public: Vector() { } Vector(int x, int y) : Tuple(x, y) { } Point &asPoint() { return reinterpret_cast(*this); } Point toPoint() const { return Point(x, y); } }; Vector Point::operator-(const Point &other) const { return Vector(x-other.x, y-other.y); } inline bool foo(int x, Point a) { if (a.x<=x) { return false; } return true; } int main() { Point a; a.x = 1; a.y = 2; time(0); int n = 0; for (int y=0; y<=44; y++) { if (foo(3, (a-a).asPoint())) { n++; } } return n; }