C++ Logo

sg14

Advanced search

Re: [SG14] Challenging the deprecation of volatile compound statements

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Fri, 12 Feb 2021 09:45:48 -0500
On Thu, Feb 11, 2021 at 7:29 PM Paul M. Bendixen via SG14 <
sg14_at_[hidden]> wrote:

> Ok so I tried
> This is absolutely the first draft of a draft, or what i like to call
> the braindump edition. Formatting is horrible and wording could
> probably also be improved.
> Please let me know what you think and where it could be improved,
> especially if there are things I have omitted.
>

Besides a paper number, it also needs *your email address* for people to
send feedback.

The "Introduction" should be massively tersened:

*C++20 deprecated many functionalities of `volatile`, due to JF
Bastien's P1152R4 [Bastien, 2019]. The rationale was to prevent programmers
misunderstanding the meaning of `volatile` in threading code [Bastien,
2018]. While this rationale is sound, the deprecation of the compound
assignment operators for `volatile` caused some response from the embedded
community [Ooijen, 2020]. We propose to restore the compound assignment
operators.*

(Even simple refactorings like replacing "It is the hope of the author
that" with "It is the author's hope that" with "It is my hope that" with "I
hope that" can lighten the load. But in this case, I don't even think it
makes sense to say "I hope we can reach a compromise position" — compromise
for the sake of compromise is never the goal! The goal is to get into
whatever state satisfies your technical needs.)

---
> This is further amplified by the fact that vendors may supply macros for
setting or clearing bits...
If vendors actually do use `|=` and `&=` for setting and clearing bits,
then just say
> Vendors provide macros that use `|=` and `&=` on volatile objects. For
example:
> - RabbitFrog RTOS has a macro `FOO_SET_FLAG(y) (mm_flags[y >> 4]) |=
(1<<(y & 0xF))`
> - FrogRabbit RTOS has a macro `BAR_SET_MEM(x, y) *(volatile int*)y &= x`
You definitely need examples, because the null hypothesis here is that
people *don't* do this.
---
> The argument against compound operations on volatile variables is that it
leads the programmer to believe that the operation itself becomes
compounded and therefore atomic. This is of course false (even though
architectures where this is the case could be imagined).
FWIW, my understanding is that the operation itself *is* frequently atomic,
or at least divvied up differently from the non-volatile version.
For example, on x86-64 Clang:
https://godbolt.org/z/q5bn5K
Non-volatile &= produces a load, modify-in-register, and store.
Volatile &= produces a modify-in-memory, followed by a load (if the final
value is needed by the expression).
Vice-versa, sometimes the compiler enforces that volatile reads/writes are
always done at the size of the data type (e.g. it is forbidden to access
the low-order byte of a `volatile int` via a byte-sized load instruction),
which can lead to more codegen than the non-volatile version.
https://godbolt.org/z/sxx1aG
> Now since there must always be a read, modify, write cycle it is possible
that an interrupt would happen in between the read and the write, however
most code that uses this idiom needs to take care of this by being right by
construction i.e. not bit-twiddeling variables that are used in the
interrupt service routines (ISRs). While there is probably some code found
in the wild where this would indicate a problem, since this is so common in
all these cases the construct would only be replaced by the equivalent
non-compound statement, giving no benefit at all.
Your use of the passive voice in the last sentence hurts you. "Would only
be replaced" by whom?— the compiler, or the human programmer? If the human
programmer is supposed to replace `x &= y` with `x = x & y` in order to
avoid ISR/multithreading bugs, then *good*, that's exactly what C++20 wants
them to do. IOW, I don't understand the thrust of this paragraph.
----
> Table 1: Occurrences of compound operations on variables typically
associated with volatile
The caption of this table doesn't match the description in the English
text. These aren't occurrences of compound assignments on *typically
volatile variables* — they're literally just all the compound assignments
in the entire program!  Most of these assignments probably don't involve
volatile variables at all.
> but the usage of this idiom also prevents adopters of C++ to use the C
libraries provided with their toolchains
I would like to see evidence of this. For example, you could provide a
short "hello world"-style program (using some C library provided with your
toolchain) and point to the line that would be deprecated in C++20.
----
> The simplest possible change that could possibly work would be to remove
the text added to paragraph [expr.ass] point 6 as this would allow compound
statements on volatile varaiables.
Nit: "varaiables".
I would like to see the specific wording proposal here, formatted similarly
to
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2266r0.html#wording
if possible.
IIUC, it's this:
A simple *An* assignment whose left operand is of a volatile-qualified type
is deprecated ([depr.volatile.type]
<http://eel.is/c++draft/depr.volatile.type>) unless the (possibly
parenthesized) assignment is a discarded-value expression or an unevaluated
operand. <http://eel.is/c++draft/expr.ass#5.sentence-1>
The behavior of an expression of the form E1 *op*= E2 is equivalent to E1 =
 E1 *op* E2 except that E1 is evaluated only once.
<http://eel.is/c++draft/expr.ass#6.sentence-1>
Such expressions are deprecated if E1 has volatile-qualified type; see
[depr.volatile.type] <http://eel.is/c++draft/depr.volatile.type>.
<http://eel.is/c++draft/expr.ass#6.sentence-2>
For += and -=, E1 shall either have arithmetic type or be a pointer to a
possibly cv-qualified completely-defined object type.
<http://eel.is/c++draft/expr.ass#6.sentence-3>
In all other cases, E1 shall have arithmetic type.
<http://eel.is/c++draft/expr.ass#6.sentence-4>
----
Personally, I'd like to see a separate section of the paper devoted to
explaining
- What do we think the semantics of `x &= y` are, when `x` is volatile? Is
it a load-modify-store? Is it an atomic modify-in-place? Is it *allowed* to
be an atomic modify-in-place? Is it allowed to use special crazy
flag-setting instructions? Basically what are programmers hoping to get
when they write that line of code, and, what are they thinking they
*mustn't* get?
- If the answer to the first question is "We don't know, it's
implementation-defined or worse, but as embedded programmers we just need
the syntax to keep compiling so that our code doesn't break," then that's
an honest answer (I guess) but it needs to be really clearly stated in this
separate section.
My goal with the separate section would be to try not to devolve the
discussion into a flamewar about the (lack of) meaning of `volatile` in
general, but to clearly indicate to the reader that you've *thought* about
its (lack of) meaning.
----
Oh, and finally, consider the following workaround:
    *(int*)myvolatilevar &= 255;
Compound assignments on non-volatile variables aren't deprecated; so if you
really need the compound stuff more than any particular semantics provided
by the volatile stuff, could you just add the cast?
–Arthur

Received on 2021-02-12 08:46:02