Undefined behavior due to 6.5.16.1p3

2015-03-09 Thread Robbert Krebbers
I was wondering whether GCC uses 6.5.16.1p3 of the C11 standard as a 
license to perform certain optimizations. If so, could anyone provide me 
an example program.


In particular, I am interested about the "then the overlap shall be 
exact" part of 6.5.16.1p3:


  If the value being stored in an object is read from another
  object that overlaps in any way the storage of the first
  object, then the overlap shall be exact and the two objects
  shall have qualified or unqualified versions of a compatible
  type; otherwise, the behavior is undefined.


Re: Undefined behavior due to 6.5.16.1p3

2015-03-10 Thread Robbert Krebbers

Dear Richard,

On 03/10/2015 09:51 AM, Richard Biener wrote:

struct X { int i; int j; };

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

will optimize to return 1.  If *p and *q were allowed to overlap
(&p->i == &q->j)
this would invoke undefined behavior.

Thanks for the example!

I guess you are considering the case where q.j and p.i overlap. For example:

  int main() {
assert(sizeof(struct X) == 2 * sizeof(int));
unsigned char *p = malloc(3 * sizeof(int));
return foo ((struct X*)(p + sizeof(int)), (struct X*)p);
  }

In a naive memory model, one would indeed expect this program to return 
0 instead of 1 (which GCC does).


However, this program already invokes undefined behavior due to C11's 
notion of effective types, namely 6.5p6 and 6.5p7.


So, let me rephrase my question. Is anyone aware of situations in which 
GCC uses 6.5.16.1p3 as a license to perform certain optimizations where 
effective types alone do not suffice.


Robbert


Re: Undefined behavior due to 6.5.16.1p3

2015-03-10 Thread Robbert Krebbers

On 03/10/2015 05:18 PM, Martin Sebor wrote:

I suspect every compiler relies on this requirement in certain
cases otherwise copying would require making use of temporary
storage. Here's an example:
Thanks, this example is indeed not already undefined by effective types, 
nor 6.2.6.1p6.


An entirely worked out example is:

#include

struct A { int a [32]; };

union {
  struct A a;
  struct { char b1; struct A b2; } b;
} u;

void init(struct A *p) {
  for (int i = 0; i < 32; i++) { p->a[i] = i; }
}

int test(struct A *p) {
  int b = 0;
  for (int i = 0; i < 32; i++) {
printf("%d=%d\n", i, p->a[i]);
if (p->a[i] != i) b = 1;
  }
  return b;
}

int main() {
  init(&u.a);
  u.b.b2 = u.a;
  return test(&u.b.b2);
}

The return value is 1 instead of 0 when compiled with GCC and clang with 
optimizations enabled.


Re: Undefined behavior due to 6.5.16.1p3

2015-03-10 Thread Robbert Krebbers

On 03/10/2015 05:44 PM, Robbert Krebbers wrote:

On 03/10/2015 05:18 PM, Martin Sebor wrote:

I suspect every compiler relies on this requirement in certain
cases otherwise copying would require making use of temporary
storage. Here's an example:

Thanks, this example is indeed not already undefined by effective types,
nor 6.2.6.1p6.
Now to make it more subtle. As far as I understand 6.5.16.1p3, undefined 
behavior can already occur without the use of union types or malloc. For 
example:


  struct S { int x, y; };
  int main() {
struct S p = (struct S){ .x = 10, .y = 12 };
p = (struct S){ .x = p.x, .y = 13 };
return p.x;
  }

is undefined AFAIK.

Is anyone aware of an example program that does not use unions or 
malloc, but where GCC performs optimizations justified by only 
6.5.16.1p3 of C11?


Re: Undefined behavior due to 6.5.16.1p3

2015-03-11 Thread Robbert Krebbers

Dear Joseph,

On 03/10/2015 11:01 PM, Joseph Myers wrote:

and did "u.b.b2 = f (u.a);" instead of "u.b.b2 = u.a;", that would not be
undefined (see 6.8.6.4 and GCC PR 43784).

Thanks for the references, those are useful!

But what about "long long" on 32 bits machines. For example:

union {
  long long a;
  struct { char b1; long long b2; } b;
} u;

Will GCC perform similar optimizations as for the case of big structs? I 
tried to play around with long long in Martin's example, but failed to 
trigger "unexpected" behaviors in GCC.


Robbert


Re: Undefined behavior due to 6.5.16.1p3

2015-03-11 Thread Robbert Krebbers

On 03/11/2015 05:31 PM, Vincent Lefevre wrote:

I disagree that it is an extension. The standard does not say
that "one union member can be active at any time".

The interpretation under which this is allowed in confirmed by
Note 95 of 6.5.2.3p3.
Effective types disallow to access a union member other than the current 
one arbitrarily, so naively effective types contradict note 95 of 6.5.2.3p3.


I think the GCC interpretation makes sense. See the following excerpt 
from the "-fstrict-aliasing" description in the gcc man page:



Allow the compiler to assume the strictest aliasing rules applicable to the language being compiled.  For C (and C++), 
this activates optimizations based on the type of expressions.  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.  For example, an 
"unsigned int" can alias an "int", but not a "void*" or a "double". A character 
type may alias any other type.

Pay special attention to code like this:

union a_union {
  int i;
  double d;
};

int f() {
  union a_union t;
  t.d = 3.0;
  return t.i;
}

The practice of reading from a different union member than the one most recently written 
to (called "type-punning") is common.  Even with -fstrict-aliasing, 
type-punning is allowed, provided the memory is accessed through the union type.  So, the 
code above works as expected.However, this code might not:

int f() {
  union a_union t;
  int* ip;
  t.d = 3.0;
  ip = &t.i;
  return *ip;
}

Similarly, access by taking the address, casting the resulting pointer and 
dereferencing the result has undefined behavior, even if the cast uses a union 
type, e.g.:

int f() {
  double d = 3.0;
  return ((union a_union *) &d)->i;
}