Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

volatile bitfield compound assignment

18 views
Skip to first unread message

Al Grant

unread,
Apr 19, 2010, 3:36:46 PM4/19/10
to
Suppose you have a volatile location
struct { int x:4; int y:4; } volatile reg;
I have a query about the required implementation of
reg.x |= 1;

6.5.16.2 states that "E1 op= E2 differs from the
simple assignment expression E1 = E1 op (E2)
only in that the lvalue E1 is evaluated only once".

By "evaluated only once" it is talking about the
evaluation of the lvalue (i.e. designator) - it doesn't
say anything about the evaluation of the lvalue's
value, and one wouldn't expect it to, as it's only in
the volatile bitfield case that it matters.

So does that mean, where
reg.x = 1;
is implemented as a read-modify-write (because
this is a bitfield), then for
reg.x |= 1;
not to be observably different from
reg.x = reg.x | 1;
it must be implemented as two reads and a write?
Intuitively, compound assignments of bitfields need
only one read, and one would hope the standard
allows an implementation to do that, and (because
we don't want vagueness about volatile) go further
and require it. But 6.5.16.2 seems to forbid it.

Tim Rentsch

unread,
Apr 22, 2010, 3:04:16 PM4/22/10
to
Al Grant <alg...@myrealbox.com> writes:

There are different opinions about what the Standard actually
allows or requires for 'reg.x |= 1;'. I think there's general
agreement that 'reg.x = reg.x | 1;' is one read access and one
write access (including if reg.x is volatile). However, for any
use of volatile it's also true that "what constitutes an access"
is implementation-defined. My best understanding is that the
Standard is expected to be read as saying it's up to each
implementation whether 'reg.x |= 1;' will be (1) one combined
read-modify-write access, or (2) two accesses, one read, one
write, or possibly (3) three accesses, one read, one write, one
read (or perhaps something else). But not everyone agrees. So
if it's important to you which result you get it's probably
better to avoid compound assignment operators, such as |=, in
conjunction with volatiles (and also not use assignment into a
volatile where the result of the assignment expression is used).
Since what happens is either unclear or implementation-defined
in such cases, it seems better just to avoid them.

Al Grant

unread,
Apr 23, 2010, 5:12:16 AM4/23/10
to
On 22 Apr, 20:04, Tim Rentsch <t...@alumni.caltech.edu> wrote:
> There are different opinions about what the Standard actually
> allows or requires for 'reg.x |= 1;'.  I think there's general
> agreement that 'reg.x = reg.x | 1;'  is one read access and one
> write access (including if reg.x is volatile).

The point is if it's a bitfield then on a target without
bit-addressability, simply writing to reg.x would likely
need a read first. So given that "reg.x = E" is in general
a read/write just to write reg.x, and given that there will be
an additional read of reg.x if it occurs in E, that ends up
with read, read, write for
reg.x = reg.x | 1;
The question is, does the Standard's claim about the
equivalence of
reg.x |= 1;
to the above expression, require it to be implemented
equivalently, including the two reads, ending up with a
Read, Read, Write sequence?

> However, for any
> use of volatile it's also true that "what constitutes an access"
> is implementation-defined.

There are any number of ways an implementation could
define how a volatile bitfield is written to. For example:
- as a read-modify-write of the container
- an "OR immediate" / bit set on the container
- as a single-bit store (on a bit-addressible machine)

Having done that, we know what is involved in doing
reg.x = 1;

Also, the implementation will define what is involved in
reading reg.x as an rvalue (likely an extra read).
So overall, the sequence of actions for
reg.x = reg.x | 1;
will be the sequence of actions to evaluate reg.x
followed by the sequence of actions to write reg.x,
whether that is a read + read/write, or a read +
bit set or a read + single-bit store or whatever.

The question is, does 6.5.16.2 require
reg.x |= 1;
to have the _same_ sequence? My reading is it does.
Which is not what people expect or want.

> if it's important to you which result you get it's probably
> better to avoid compound assignment operators

Unfortunately implementors don't have that luxury.

Tim Rentsch

unread,
Apr 23, 2010, 6:26:40 AM4/23/10
to
Al Grant <alg...@myrealbox.com> writes:

Ahhh. Now that I read this response, I have a better idea of
what you're asking and why. Let me try again and see if I can't
do better this time around.

1. I'm not the right person to give an authoritative answer to
these questions. Now, having said that, we proceed on to

2. My best understanding is that 'reg.x |= 1;' may be implemented
either as the same access sequence as 'reg.x = reg.x | 1;' or as
a single read-modify-write for the compound assignment operator
'|=', using the read access both to get the old value of 'reg.x'
and to get the surrounding bits that need to be re-written.

3. Again my best understanding -- because the question is being
asked from the point of view of an implementor, you're in the
drivers seat. The "implementation-defined" proviso for accesses to
volatile gives you freedom to choose either of the alternatives
listed in (2), as you wish (or almost other crazy thing you can
think of, but you were asking just about these two). All that you
have to do to exercise this great freedom of choice is document what
the rules are. Again, to be specific, the rule for volatile access
can choose to define volatile access differently for compound
assignment operators and simple assignment, and can take into
account whether the left-hand-side expression also appears on the
right-hand-side. Document your choice, and you can do whichever you
think is best (or will make your users happiest).

4. To repeat myself, these statements are my best understanding
of what the Standard requires, but I am not the right person
to give authoritative answers to these questions.

5. Probably this is obvious but I feel it should be added --
although the Standard may grant license to do all sorts of weird
things, not everyone thinks that just because the Standard allows
something that means the something is acceptable. So even if you
decide that the Standard allows a specific interpretation, you
might still choose not to follow some particular interpretation
based on these other sorts of concerns.

So to sum up I think you're getting the answer you wanted
to get rather than the answer you were expecting to get,
but please make an effort to verify that with other sources
before committing yourself down that path.

Jun Woong

unread,
Apr 28, 2010, 11:05:47 AM4/28/10
to
Al Grant <algr...@myrealbox.com> wrote:
> The point is if it's a bitfield then on a target without
> bit-addressability, simply writing to reg.x would likely
> need a read first. ?So given that "reg.x = E" is in general

> a read/write just to write reg.x,

This reminds me of a client's request I met when I was maintaining a
compiler for a small machine. The target machine had instructions for
byte access, but he asked to emit word-access instructions for 8-bit
bit-fields of a volatile struct to make the word-sized storage units
containing them read (I don't remember the reason he needed such a
behavior).

> and given that there will be
> an additional read of reg.x if it occurs in E, that ends up
> with read, read, write for

> ? reg.x = reg.x | 1;


> The question is, does the Standard's claim about the
> equivalence of

> ? reg.x |= 1;


> to the above expression, require it to be implemented
> equivalently, including the two reads, ending up with a
> Read, Read, Write sequence?

My understanding is yes. Given E1 op= E2, if E1 has no visible side
effect, it is completely up to a compiler how to handle it as long as
the final result does not change. With volatile accesses, I think a
compiler is very careful to follow what is specified in the standard
when choosing instructions to be emitted; my belief is that the only
possible difference of E1 op= E2 from E1 = E1 op E2 should be
omitting one of two occurrences of a visible side effect triggered
whenever reg.x is evaluated if any.

For another example, there was a discussion about an implementation
optimizing two accesses to a volatile object between two
adjacent sequence point and, as I recall, the conclusion was that a
conforming implementation has to make two accesses (if they are
distinguishable) since the standard describes it is accessed twice
in the abstract machine.

[...]


> Having done that, we know what is involved in doing

> ? reg.x = 1;


>
> Also, the implementation will define what is involved in
> reading reg.x as an rvalue (likely an extra read).
> So overall, the sequence of actions for

> ? reg.x = reg.x | 1;


> will be the sequence of actions to evaluate reg.x
> followed by the sequence of actions to write reg.x,
> whether that is a read + read/write, or a read +
> bit set or a read + single-bit store or whatever.
>
> The question is, does 6.5.16.2 require

> ? reg.x |= 1;
> to have the _same_ sequence? ?My reading is it does.


> Which is not what people expect or want.

Do you know why they do not expect or want that even if many
implementers are as conservative as possible in implementing volatile
accesses?


--
Jun, Woong (woong.jun at gmail.com)

0 new messages