curious "apparent" gcc bug that clang seems to get right

I'm not sure myself enough of how, if at all, volatile is inherited into a class from it's members.
But the following code, boiled down from some complicated android code is essentially.

This same problem happens for gcc x86 and mips.

Anyone care to weigh in on a proper reading of the C or C++ standard for this?

Clang does what not have this problem.

/* mips-linux-gnu-gcc -S a.c -O3 */
/* gcc -S a.c -O3 */

union {
         volatile int y;
} i, j;

void works_as_expected(void) {
         do {
                 /* reads j, writes i */
                 i.y = j.y;
         } while (1);
}

void is_this_correct(void) {
         do {
                 /* mips: writes j, doesn't reread i */
                 /* x86: rereads j, doesn't write i */
                 i = j;
         } while(1);
}

I'm not sure myself enough of how, if at all, volatile is inherited into a
class from it's members.
But the following code, boiled down from some complicated android code is
essentially.

This same problem happens for gcc x86 and mips.

Anyone care to weigh in on a proper reading of the C or C++ standard for
this?

Clang does what not have this problem.

/* mips-linux-gnu-gcc -S a.c -O3 */
/* gcc -S a.c -O3 */

union {
        volatile int y;
} i, j;

void works_as_expected(void) {
        do {
                /* reads j, writes i */
                i.y = j.y;
        } while (1);
}

void is_this_correct(void) {
        do {
                /* mips: writes j, doesn't reread i */

I assume you meant "writes i, doesn't reread j"?

                /* x86: rereads j, doesn't write i */
                i = j;
        } while(1);
}

I don't think there is anything to really be gleaned from the standard
here. It's largely up to the implementation how they want to interpret the
meaning of volatile.

That said, I don't think this interpretation is even remotely useful. I
think it's just a bug. I think the useful interpretation would be the
obvious one based on the reading of aggregate implicit copy assignment
which is to perform member-wise assignment. In turn, that would produce the
same code as your first function if implemented correctly.

If Clang is getting this wrong, I think it's a Clang bug.

Clang gets this right. :slight_smile:

C++11 makes this easier to follow. The case this boils down to is the defaulted copy assignment operator for union like classes:
The implicitly-defined copy assignment operator for a union X copies the object representation (3.9) of X.

As a result, the fact that the member y is volatile is completely ignored in C++. C11 is much less clear on the subject, but as far as I can make it, the assignment operator is considered to copy the union object and not the volatile int subobject contained within, although you could easily hide behind section 6.7.3's note that "what constitutes an access to an object that has volatile-qualified type is implementation-defined."

FWIW, this apparently has been discussed a lot in gcc land recently and they are making changes in 4.9

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=47409

Yeah, the C standard is pretty vague here, but I think the obvious intent is that an access to the aggregate is simultaneously an access to its members (all of them, in the case of a struct, or just to its active member, in the case of a union), and therefore the portion of that access which touches a volatile member is required to follow volatile semantics, i.e. to actually occur.

Note that the C++ rule differs subtly from the C rule, e.g.:
  union { volatile int x; } z;
  ...
  z;
z is not itself volatile and therefore does not undergo l-value-to-r-value conversion even in C++11, whereas in C it both undergoes formal conversion and is arguably unoptimizable.

I suppose that, in theory, this interpretation would allow a compiler to remove an unused load of a union containing a volatile member if it can prove that that the active member is not volatile.

John.