| ▲ | mananaysiempre 2 hours ago |
| And it makes sense as long as you allow the concept of unsequenced operations at all (admittedly it’s somewhat rare; e.g. in Scheme such things are defined to still occur in sequence, but which specific sequence is unspecified and potentially different each time). The “volatile” annotation marks your variable as being an MMIO register or something of that nature, something that could change at any point for reasons outside of the compiler’s control. Naturally, this means all of the hazards of concurrent modification are potentially there. That said, your “common parlance” definition of “data race” is not the definition used by the C standard, so your last sentence is at best misleading in a discussion of standard C. > The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior. (Here “conflicting” and “happens before” are defined in the preceding text.) |
|
| ▲ | tsimionescu an hour ago | parent [-] |
| Your first paragraph makes it sound as if the compiler will actually generate two reads of the value of some register, which might lead to unexpected effects at runtime for certain special registers. However, this is not at all what UB means in C (or C++). The compiler is free to optimize away the entire block of code where this printf() sequence occurs, by the logic that it would be UB if the program were to ever reach it. For example, the following program: int y = rand();
if (y != 8) {
volatile int x;
printf("%d: %d", x, x) ;
} else {
printf("y is 8");
}
Can be optimized to always print "y is 8" by a perfectly standard compliant compiler. |
| |
| ▲ | shakna 44 minutes ago | parent [-] | | "volatile" tells the compiler it is _not_ safe to optimise away any read or write, so it can't just optimise that section away at all. > An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.3. Furthermore, at every sequence point the value last stored in the object shall agree with that prescribed by the abstract machine, except as modified by the unknown factors mentioned previously. A compliant compiler is only free to optimise away, where it can determine there are no side-effects. But volatile in 5.1.2.3 has: > Accessing a volatile object, modifying an object, modifying a file, or calling a function that does any of those operations are all side effects. | | |
| ▲ | rcxdude 31 minutes ago | parent | next [-] | | Yes, but undefined behaviour is undefined behaviour, and that behaviour can legally be that the code is not emitted at all, volatile (or any other side effect) or not. (and compilers do reason about undefined behaviour when optimising, so this isn't necessarily a completely theoretical argument, though I don't know whether the in compiler's actual logic which of 'don't optimise volatile' or the 'do assume undefined behaviour is impossible and remove code that definitely invokes it' would 'win', or whether there's any current compiler that would flag this as unconditionally undefined behaviour in the first place). | | |
| ▲ | shakna 25 minutes ago | parent [-] | | Volatile wins. GCC calls that out [0] - volatile means things in memory may not be what they appear to be, and that there are asynchronous things happening, so something that may not appear to be possible, may become so, because volatile is a side-effect. So about the only optimisation allowed to happen, is combining multiple references. Clang is similar: > The compiler does not optimize out any accesses to variables declared volatile. The number of volatile reads and writes will be exactly as they appear in the C/C++ code, no more and no less and in the same order. [0] https://www.gnu.org/software/c-intro-and-ref/manual/html_nod... |
| |
| ▲ | u8080 22 minutes ago | parent | prev | next [-] | | When compiler decides something is UB aka "result of this code is not defined and could be any" it selects the most performant version of undefined behavior - doing nothing by optimizing code away. | | |
| ▲ | shakna 11 minutes ago | parent [-] | | The compiler is not free to remove accesses to something marked volatile - its defined as a side-effect. Volatile means something else may be acting here. Something else may install anything into the register at any time - and every time you access. The compiler is required to preserve the order of accesses. In almost every C compiler, today, there are almost no optimisations the moment a volatile is introduced, for this reason. |
| |
| ▲ | saagarjha 33 minutes ago | parent | prev [-] | | Sure it can. That code path has unconditional UB and thus it is not valid. | | |
|
|