strict aliasing: cast from char[] or char *

2011-06-06 Thread Herman, Geza

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 *

2011-06-07 Thread Herman, Geza

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 *

2011-06-07 Thread Herman, Geza

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

2007-06-19 Thread Herman Geza
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

2007-06-26 Thread Herman Geza
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

2007-06-26 Thread Herman Geza

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

2007-06-26 Thread Herman Geza


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

2007-06-26 Thread Herman Geza

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

2007-06-26 Thread Herman Geza

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

2007-06-26 Thread Herman Geza


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

2007-06-29 Thread Herman Geza

> 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;
}