On 10/27/2017 04:54 PM, Richard Biener wrote:
> On Fri, Oct 27, 2017 at 3:00 PM, Yubin Ruan <[email protected]> wrote:
>> +Cc gcc-list.
>>
>> Does any gcc developer have any comments?
>
> See PR82224. The code is valid.
>
>> On Mon, Sep 25, 2017 at 01:41:55PM -0700, Myriachan wrote:
>>> This question that "supercat" posted on Stack Overflow ran into an
>>> interesting problem:
>>>
>>> https://stackoverflow.com/questions/46205744/is-this-use-of-unions-strictly-conforming/
>>>
>>> A copy of the code involved is as follows:
>>>
>>> struct s1 {unsigned short x;};
>>> struct s2 {unsigned short x;};
>>> union s1s2 { struct s1 v1; struct s2 v2; };
>>>
>>> static int read_s1x(struct s1 *p) { return p->x; }
>>> static void write_s2x(struct s2 *p, int v) { p->x=v;}
>>>
>>> int test(union s1s2 *p1, union s1s2 *p2, union s1s2 *p3)
>>> {
>>> if (read_s1x(&p1->v1))
>>> {
>>> unsigned short temp;
>>> temp = p3->v1.x;
>>> p3->v2.x = temp;
>>> write_s2x(&p2->v2,1234);
>>> temp = p3->v2.x;
>>> p3->v1.x = temp;
>>> }
>>> return read_s1x(&p1->v1);
>>> }
>>> int test2(int x)
>>> {
>>> union s1s2 q[2];
>>> q->v1.x = 4321;
>>> return test(q,q+x,q+x);
>>> }
>>> #include <stdio.h>
>>> int main(void)
>>> {
>>> printf("%d\n",test2(0));
>>> }
>>>
>>>
>>> Both GCC and Clang in -fstrict-aliasing mode with optimizations are acting
>>> as if they ran into undefined behavior, and return 4321 instead of the
>>> expected 1234. This happens in both C and C++ mode. Intel C++ and Visual
>>> C++ return the expected 1234. All four compilers hardwire the result as a
>>> constant parameter to printf rather than call test2 or modify memory at
>>> runtime.
>>>
>>> From my reading of the C++ Standard, particularly [class.union]/5,
>>> assignment expressions through a union member access changes the active
>>> member of the union (if the union member has a trivial default constructor,
>>> which it does here, being C code). Taking the address of p2->v2 and p1->v1
>>> ought to be legal because those are the active members of the union at the
>>> time their pointers are taken.
>>>
>>> Is this a well-defined program, or is there subtle undefined behavior
>>> happening here?
Thanks.
As put in my stackoverflow answer[1], I believe this behavior is specified in
the GCC-online-doc, in section `-fstrict-alisgin'[2]:
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.
See Structures unions enumerations and bit-fields implementation.
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;
}
The -fstrict-aliasing option is enabled at levels -O2, -O3, -Os.
I think the second example above is similar to the OP's question (although the
C standard might not say so... so from my perspective if the second example
above is true, the OP's code is invalid.
Do anyone have any comment?
Yubin
[1]:
https://stackoverflow.com/questions/46205744/is-this-use-of-unions-strictly-conforming/46968235?noredirect=1#comment80930683_46968235
[2]: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html