C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Use of volatile as function argument should not be deprecated.

From: Hyman Rosen <hyrosen_at_[hidden]>
Date: Thu, 9 Jun 2022 11:44:49 -0400
The problem started with code like

    volatile char v[4] = 0;
    ++v[2];

On architectures that do not provide byte-level memory access, the abstract
machine cannot be followed exactly. Reading or writing one bye from that
array could require reading or writing all four. Rather than trying to
come up with wording to cover such situations, the C standard made volatile
access implementation-defined, with the understanding that compilers should
implement volatile semantics as best they can in weird situations. But
once the optimizationists captured the standardization processes, they took
that implementation-defined behavior as permission to disregard volatile
semantics altogether even on normal platforms.

Here is an interesting case.

    void foo(int *p) {
        if (!p) { printf("null pointer\n"); }
        volatile bool b = false;
        if (b) { abort(); }
        *p = 0;
    }

It's implementing a poor-man's contract check that the argument is not null
and trying to print a message if it is (but continue going). With
correctly implemented volatile semantics, the message will print because
printing is a side effect that must happen before the volatile access which
is also a side effect. But the Microsoft compiler elides the volatile
variable and test altogether, then sees that if the initial test is true
then undefined behavior must result, and eliminates that test and the
print.

On Thu, Jun 9, 2022, 11:11 AM Ryan P. Nicholl via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> I am not sure if this logic is correct. volatile implies that the variable
> may be changed externally. I don't think there is any rule that says this
> only applies if the compiled code exports the address of the variable. An
> external change by the debugger is included in the scope of volatile.
> volatile implies that implementation details allow the variable to change
> in a way the compiler can't understand. Or at least it did before they
> started changing the wording. If they change it, I'll have to re-read it
> for the new e.g. C++23 version.
>
> ThreadSanitizer helped me catch all the incorrect volatile uses in our
> code. I think that promoting tools like these is a better solution than
> disallowing volatile to be used as an interface to the debugger.
>
> Atomics *don't work* here. The compiler could see that the atomic object
> cannot be accessed by anything else in the abstract machine because the
> address isn't exported. Then optimize it away. Only volatile, which tells
> the compiler that something *outside the C++ abstract machine* could modify
> the variable can satisfy this use case.
>
> I can probably make a custom class which uses inline assembly in the worst
> case. But that would replace code that works with all gnu gcc/llvm
> clang/apple clang/microsoft msvc compilers in vs debugger, lldb, and gdb
> debuggers, and force compiler specific rewrites and architecture specific
> rewrites. Today the simple use of volatile works *everywhere*. *All* major
> compilers support this now and the standard shouldn't drop support for this
> unless a very good standardized alternative is introduced that doesn't
> require a bunch of #ifdef garbage.
>
> A simple warning by a compiler when people use volatile is all that's
> needed. C++ is full of dangerous things like pointers. Please stop trying
> to slap training wheels on the C++ compiler when it interferes with
> legitimate uses.
>
>
> -------- Original Message --------
> On Jun 9, 2022, 10:15, Jason McKesson via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>
> On Thu, Jun 9, 2022 at 9:34 AM Ryan P. Nicholl via Std-Proposals
> <std-proposals_at_[hidden]> wrote:
> >
> > There is no real reason we should need to add a new feature for what
> already legitimately works today.
>
> That a thing "works" does not mean it is supposed to work this way. Or
> that it makes some kind of sense. See below.
>
> > Existing behavior of volatile was fine.
>
> My general understanding of the reasons for the deprecation of various
> uses of `volatile` was to actually *rationalize* the feature. That is,
> to make it make sense and to make sure people understand that it isn't
> meant to do a lot of things they thought they were doing with it.
>
> The principal problem with `volatile` is that it got used for a lot of
> things that it was never meant to do. People used it for atomics
> because certain platforms guaranteed atomic access if you used basic
> types that were `volatile`. That was *never* what it was meant to do
> and if you've only ever programmed on platforms where that's true, you
> will get a very rude awakening when you're forced to use ARM and find
> all your "atomic" code stops working.
>
> This debugging trick is *also* not what `volatile` was meant to do.
> And as others have pointed out, it's not even behavior that is
> required *by the standard*. Since the address of this variable is
> under the complete control of the runtime, and its address is never
> given out to any code that the compiler cannot see, the compiler can
> be 100% certain that no external process can manipulate its value.
>
> You might consider this all to be academic. But real programmers need
> to be able to read someone else's C++ and divine what it is they're
> doing from that source code. Features like `volatile` which get used
> for a lot of disparate things that don't really make sense to be under
> the same umbrella exacerbate this situation. Making it clear that
> `volatile` is a means to interact with hardware storage that changes
> outside of the C++ object model and *nothing else* would help clean up
> the language.
>
> > The new definition of volatile is considered harmful. I would hope the
> compiler vendors have the sense to ignore this nonsense and keep the old
> behavior.
> >
> > -------- Original Message --------
> > On Jun 9, 2022, 00:20, Thiago Macieira via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
> >
> >
> > On Wednesday, 8 June 2022 20:16:50 PDT Ryan P. Nicholl via Std-Proposals
> > wrote:
> > > All compilers have debuggers, I don't see why we shouldn't be able to
> use
> > > e.g. "RelWithDebInfo" and then turn on a variable. I'm pretty sure the
> > > point of volatile is to eliminate the "as-if" rule for that particular
> > > variable, meaning a debugger should be allowed to change the value and
> the
> > > compiler respect that.
> >
> > That's an incorrect understanding. The language does need not take
> debugging
> > into account. The language defines how the program runs in the abstract
> machine
> > and volatile variables are I/O -- that is, they communicate with the
> outside
> > world.
> >
> > Under the "as-if" rule, if the compiler can prove that no I/O is
> possible,
> > then it *can* remove the volatile variable completely. That's what's
> happening
> > here.
> >
> > Global variables' address can be manipulated by the linker and placed in
> MMIO
> > regions, but if this is a possibility, then it is a contract between the
> > compiler and linker. That's not possible at all for stack (automatic
> storage)
> > variables, because there's no symbol for the linker to see in the first
> place.
> > Therefore, stack variables simply cannot be volatile. The language in the
> > standard was updated to clarify this situation, allowing the compilers
> to do
> > what should have been there all along.
> >
> > > A register can sure be volatile if it tells the compiler not to
> optimize
> > > away accesses or uses of that register, since the program's execution
> could
> > > be stopped and then a debugger simply rewrite the variable.
> >
> > There's no such thing as optimisation in the standard. If you want to
> control
> > what the optimiser of your compiler does or does not do, you need to
> discuss
> > it with your compiler vendor.
> >
> > Volatile *general purpose* registers make no sense. Some architectures
> do have
> > such a thing as I/O via registers, but compilers would never allocate a
> > variable to them, exactly because their use would have side-effect.
> Conversely,
> > compilers always use registers that don't have side-effects
> >
> > In a debugger, you could rewrite a variable, indeed, but that doesn't
> mean
> > your program will proceed to run correctly. The fact that a variable has
> a
> > current storage location somewhere your debugger can modify it does not
> mean
> > it's the only copy or that knowledge about it wasn't used in other
> > optimisations, prior to that point or after it.
> >
> > > I therefore see a perfectly legitimate way for a function parameter to
> be
> > > volatile? It certainly isn't useful to have stack volatiles for
> hardware
> > > programming but I have found the current implementation which allows
> it for
> > > communication between the debugger and the program to be quite useful.
> I'm
> > > not sure if the standard requires this behavior but I believe it does.
> >
> > I do not see it as legitimate. In your own words: "it isn't useful to
> have
> > stack volatiles for hardware programming". That's what volatile means
> today
> > and that's what the standard removed. Asking that it be reversed in a
> defect
> > report implies that this determination was wrong -- and it isn't.
> >
> > I see a perfectly legitimate use-case in what you're asking, though. You
> may
> > want to write a paper to give new meaning to the volatile keyword and
> address
> > your use-case, but that's not the same as what you're asking right now.
> >
> > I think your best bet is to discuss this with your compiler vendor and
> have
> > them provide a way for you to annotate somehow the sources and indicate a
> > variable must be spilled to memory and that the compiler must disable
> > optimisations that would assume its value stays unchanged while it is
> there.
> > They may decide to use the volatile marker as an extension to the
> standard.
> >
> > > Another case is that if we did have a "volatile sig_atomic_t" as a
> function
> > > parameter then we can pass that address to a global variable and
> install a
> > > signal handler that uses it. There's no rule against taking the
> address of
> > > a function argument and the calling convention is pretty irrelevant for
> > > this purpose. I would suppose a volatile argument behaves the same way
> as
> > > const, irrelevant for the caller but relevant for the definition.
> >
> > You don't need a volatile parameter for that. You can create a local
> variable
> > for it. They cost exactly the same.
> >
> > volatile atomics (not sig_atomic_t) are practically the only valid use of
> > volatile. And besides communicating with peripheral hardware via MMIO,
> > asynchronous signal handlers are the only remaining situation where even
> > volatile atomics make sense. They're subtly different to modifications
> by other
> > threads of execution. This is an area of the standard that still has some
> > rough edges.
> >
> > --
> > Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
> > Software Architect - Intel DPG Cloud Engineering
> >
> > --
> > Std-Proposals mailing list
> > Std-Proposals_at_[hidden]
> > https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> >
> > --
> > Std-Proposals mailing list
> > Std-Proposals_at_[hidden]
> > https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
>

Received on 2022-06-09 15:45:04