Bug 50284 - possible miscompilation with -fstrict-aliasing
Summary: possible miscompilation with -fstrict-aliasing
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: unknown
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-09-03 18:49 UTC by Rafael Avila de Espindola
Modified: 2011-09-04 08:57 UTC (History)
3 users (show)

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


Attachments
testcase (339 bytes, text/plain)
2011-09-03 18:53 UTC, Rafael Avila de Espindola
Details
a better testcase (334 bytes, text/plain)
2011-09-04 03:49 UTC, Rafael Avila de Espindola
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Rafael Avila de Espindola 2011-09-03 18:49:49 UTC
I am not sure if the attached program is valid, but I think it is covered by c99 6.5 p7. On irc pinkia points out that it might be invalid. His arguments are

* upcast is undefined in general, 6.5 p7 is trying to allow downcasting.
* upcasting is defined when the type was originally that type.

Two followup observations are that

* If we read "z->data.XXX" as an access to the member (an not the full structure), all the access in the program are of the correct type.
* On the implementation side, this "bug" show up when main is in a another translation unit too, a case where gcc could not know if "the type was originally that type".

Philip Taylor pointed me at http://davmac.wordpress.com/2010/02/26/c99-revisited/ which has an interesting discussion about "Does accessing an object constitute access to the containing object"?

This bug is "fixed" on trunk by 160947, but since that is an optimization change, it probably has just deactivated the code path that caused this behavior.

For some context, this testcase is a reduction from:

http://hg.mozilla.org/mozilla-central/file/a351ae35f2c4/js/src/jscntxtinlines.h#l179
Comment 1 Rafael Avila de Espindola 2011-09-03 18:53:58 UTC
Created attachment 25188 [details]
testcase
Comment 2 Rafael Avila de Espindola 2011-09-03 19:07:54 UTC
Forgot to mention, this only reproduces with -fPIC. So to reproduce this you need

* a linux 32 bit build older than 160947
* run cc1 with: -quiet -fPIC -O2 -std=c99
Comment 3 Richard Biener 2011-09-03 20:52:49 UTC
struct Value {
  struct jsval data;
};
...
    struct jsval y = t3.array[i];
    struct Value *z = (struct Value*)&y;
    if (z->data.tag == 0xFFFFFF85) {

that's invalid in GCCs reading of 6.5 p7. jsval is a subset of Value's alias-set
but not the other way around.  GCC reads z->data.tag as an access to an
object of type Value which is invalid.

The contorted reasoning is that the pointer conversion invokes undefined
behavior.  Definitely an interesting blog post ;)
Comment 4 Rafael Avila de Espindola 2011-09-03 22:54:10 UTC
(In reply to comment #3)
> struct Value {
>   struct jsval data;
> };
> ...
>     struct jsval y = t3.array[i];
>     struct Value *z = (struct Value*)&y;
>     if (z->data.tag == 0xFFFFFF85) {
> 
> that's invalid in GCCs reading of 6.5 p7. jsval is a subset of Value's
> alias-set
> but not the other way around.  GCC reads z->data.tag as an access to an
> object of type Value which is invalid.

So downcast (i.e. casting to a more specialized type) are invalid even if original data type is correct (not that it is in the reduced testcase)? That is really strict :-(

> The contorted reasoning is that the pointer conversion invokes undefined
> behavior.  Definitely an interesting blog post ;)

is there any hope that gcc could be made a bit less strict? Either reading the member access as not involving an access to the full object or accepting downcasts (when the original type matches) would work. My preference would be for the second option, as downcasts are fairly common in OO.
Comment 5 Rafael Avila de Espindola 2011-09-04 03:49:59 UTC
Created attachment 25189 [details]
a better testcase

This one allocates the original objects as the "derived class" Value.
Comment 6 Richard Biener 2011-09-04 08:57:35 UTC
(In reply to comment #4)
> (In reply to comment #3)
> > struct Value {
> >   struct jsval data;
> > };
> > ...
> >     struct jsval y = t3.array[i];
> >     struct Value *z = (struct Value*)&y;
> >     if (z->data.tag == 0xFFFFFF85) {
> > 
> > that's invalid in GCCs reading of 6.5 p7. jsval is a subset of Value's
> > alias-set
> > but not the other way around.  GCC reads z->data.tag as an access to an
> > object of type Value which is invalid.
> 
> So downcast (i.e. casting to a more specialized type) are invalid even if
> original data type is correct (not that it is in the reduced testcase)? That is
> really strict :-(

No, if there is an object of dynamic type Value at &y then the code is valid.
But you've stored to *&y via an lvalue of type jsval and are reading from
it via an lvalue of type Value.

> > The contorted reasoning is that the pointer conversion invokes undefined
> > behavior.  Definitely an interesting blog post ;)
> 
> is there any hope that gcc could be made a bit less strict? Either reading the
> member access as not involving an access to the full object or accepting
> downcasts (when the original type matches) would work. My preference would be
> for the second option, as downcasts are fairly common in OO.

Well, if we allow this case then we can drop any advanced TBAA we do
completely.  This restriction is really fundamental to TBAA based
optimizations.

Otherwise consider

int i;
struct X { int k; .... };

int foo(struct X *p)
{
  i = 0;
  p->k = 1;
  return i;
}

and we couldn't be sure that p->k is not accessing i and thus not optimize
the above to return 0.  That would be very bad.

You have -fno-strict-aliasing to "save" you.

Your better testcase doesn't change anything - you've just changed the
type of an unrelated object.