Undefined behavior due to 6.5.16.1p3
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
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
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
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
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
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; }