On Sep 11, 2012, at 8:19 PM, Richard Smith wrote:

> On Tue, Sep 11, 2012 at 6:47 PM, John McCall <[email protected]> wrote:
> On Sep 11, 2012, at 6:29 PM, Chandler Carruth wrote:
>> On Tue, Sep 11, 2012 at 6:09 PM, Richard Smith <[email protected]> wrote:
>> On Tue, Sep 11, 2012 at 6:04 PM, John McCall <[email protected]> wrote:
>> On Sep 11, 2012, at 4:41 PM, Richard Smith wrote:
>> > On Tue, Sep 11, 2012 at 11:33 AM, John McCall <[email protected]> wrote:
>> > On Sep 11, 2012, at 11:07 AM, Chandler Carruth wrote:
>> > > On Fri, Aug 31, 2012 at 1:01 PM, Eli Friedman <[email protected]> 
>> > > wrote:
>> > >> Another nasty case I just thought of:
>> > >>
>> > >> struct x { int i : 24; };
>> > >> struct y { int i : 24; char j; };
>> > >> union z {
>> > >>   struct x x;
>> > >>   struct y y;
>> > >> };
>> > >> union z a;
>> > >> void f(int i) {
>> > >>   a.x.i = i;
>> > >> }
>> > >> void g(char j) {
>> > >>   a.y.j = j
>> > >> }
>> > >>
>> > >> The two writes are to separate memory locations. :)
>> > >
>> > > Wait, hold on... I'm deeply confused. Maybe because I don't know how C11 
>> > > unions work?
>> > >
>> > > With C++11, only one side of the union is allowed to be active, and so I 
>> > > don't think they are separate memory locations?
>> >
>> > I agree that this isn't a problem, but the analysis is a bit more 
>> > complicated;
>> > it hinges on the fact that it's okay to *read* from an inactive union 
>> > member
>> > under the common-prefix exception, but it's not okay to *write* to it.  The
>> > same analysis applies in both standards:
>> >
>> > Is this still OK if the extra union member is volatile? Chandler and I 
>> > have discussed this, and it's far from clear that it would be. (In 
>> > particular, we can conceive of a seemingly-reasonable case where the union 
>> > sits on an MMIO port, and only the fourth byte has volatile semantics.)
>> 
>> I see no reason why making the member volatile changes anything.
>> Other members in the union can be assumed not to exist, because the
>> active union member *must* be the one we're assigning to — indeed,
>> in C such an assignment is how you change the active union member.
>> 
>> I'm talking about the load-widening case, not the store-widening. Given:
>> 
>> union U {
>>   struct X { int a : 24; volatile char b; } x;
>>   struct Y { int c : 24; } y;
>> };
>> 
>> ... if x is the active member, and we load y.c, can we really load from the 
>> storage location containing x.b?
>> 
>> After going back and forth a few times amongst the committee members here, 
>> we propose this:
>> 
>> This is a defect. We should disallow reading from the common prefix of a 
>> union using a non-active member if there are any volatile members following 
>> the common prefix. There doesn't appear to be any way to support these union 
>> types and common-prefix reads and the committee's express desire that load 
>> widening is a valid compiler optimization.
>> 
>> Reasons why this truly seems like a defect:
>> 
>> 1) We can't even load widen through the padding bytes:
>> 
>> union U {
>>   struct X { int a : 24; volatile char b; } x;
>>   struct Y { int a : 24; int c; } y;
>> };
>> 
>> 2) We can't even load widen when the next field in all members after the 
>> common prefix is non-volatile:
>> 
>> union U {
>>   struct X { int a : 33; char b; volatile char c; } x;
>>   struct Y { int a : 33; } y;
>> };
>> 
>> 
>> Seem plausible?
> 
> I could accept this as a defect.  I don't think it's required.
> 
> For example, I claim that, if we need x and z, we are allowed to use a 32-bit
> load here (although, granted, it might not be a good idea):
>   struct [[align(4)]] A {
>     char x;
>     volatile char y;
>     char z;
>   };
> 
> [The common initial sequence exemption only applies to structs as union 
> members, so that case is fine. Assuming these are each wrapped in a struct...]

I think you misread what I wrote or assumed it away.  I wrote, and meant, a 
struct;  there's no union here.  I am saying that I believe it is legal to 
widen a load so that it spans other data that happens to include a volatile 
object.  That is, it is perfectly legal to touch a volatile object when not 
requested to, as long as you (1) implement the actual requested accesses 
exactly as the abstract machine specifies, (2) don't introduce data races as 
covered by [intro.memory], and (3) don't violate your implementation-defined 
semantics for volatile accesses.

> We are allowed to do this by dint of declaring "our implementation supports
> memory-mapped I/O ports but does not define semantics for aggregates
> which partially overlap them".  Voilà, the widened load can be assumed to
> have no side effects and is therefore permitted.
> 
> I'm not convinced by your argument: as-if doesn't apply to the observable 
> behavior of the program, so we can't assume the widened load has no 
> side-effects. However, since I've reached the same conclusion via a different 
> route, I don't think it's worth debating.

This isn't as-if, it's the implementation defining some implementation-defined 
semantics.  The standard's notion of "side effects" is intentionally extremely 
vague.  We are entitled to say that certain objects can't really involve 
memory-mapped I/O.

John.
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to