Date: Thu, 09 Jun 2022 16:04:15 +0000
That's interesting. Maybe I would see this if I used different compiler options on MSVC or another version. But I don't think volatile is supposed to be a full barrier, only the volatile accesses themselves are not able to be reordered? I'm curious what version you are using here.
-------- Original Message --------
On Jun 9, 2022, 11:44, Hyman Rosen wrote:
> 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]socpp.org> 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]ocpp.org
>>>> 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]rg
>>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
-------- Original Message --------
On Jun 9, 2022, 11:44, Hyman Rosen wrote:
> 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]socpp.org> 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]ocpp.org
>>>> 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]rg
>>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
Received on 2022-06-09 16:08:54