Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can this volatile access be reordered with respect to a non-volatile access?

The below code example is from a Chinese blog which introduces the effect of volatile. The left side is the C code; the other is the generated assembly code.

// cordering.c                                gcc -O2 -S -masm=intel cordering.c

int A;
volatile int B;
void foo()                                    mov   eax, DWORD PTR B[rip]
{                                             mov   DWORD PTR B[rip], 0
    A = B + 1;                                add   eax, 1
    B = 0;                                    mov   DWORD PTR A[rip], eax
}                                             ret

As we can see in the assembly code, the side-effect of A is placed after the side-effect of B, even though B is volatile qualified. However, cppreference.com says:

[W]ithin a single thread of execution, volatile accesses cannot be optimized out or reordered with another visible side effect that is sequenced-before or sequenced-after the volatile access.

Here the side-effect of A is sequenced before B, so I think it's illegal for the compiler to do this. Am I right?


As a supplement, the blog said if we want to guarantee the sequence between a volatile and non-volatile type, we need to make both volatile:

// cordering.c                                gcc -O2 -S -masm=intel cordering.c

volatile int A;
volatile int B;
void foo()                                    mov   eax, DWORD PTR B[rip]
{                                             add   eax, 1
    A = B + 1;                                mov   DWORD PTR A[rip], eax
    B = 0;                                    mov   DWORD PTR B[rip], 0
}                                             ret
like image 547
scottxiao Avatar asked Apr 14 '18 08:04

scottxiao


Video Answer


1 Answers

The page you linked says:

Every access (read or write operation, member function call, etc.) made through a glvalue expression of volatile-qualified type is treated as a visible side-effect for the purposes of optimization (that is, within a single thread of execution, volatile accesses cannot be optimized out or reordered with another visible side effect that is sequenced-before or sequenced-after the volatile access.

Thus, if A is not volatile, accesses to A are not treated as visible side-effects, and the second statement doesn't apply (as it doesn't say anything about reordering visible and non-visible accesses).

EDIT: Note that cppreference is not official documentation for C++, but a community effort. I do believe that the intent of the statement in bold was to define what a visible side-effect is, but it is not written clearly.

The definitive reference is the C++ standard (here are some options for getting it). The N4659 standard draft, defines side-effects in [intro.execution], paragraph 14, and visible side-effects through a 2-page chain of definitions in [intro.races]. I am not an expert on the C++ standard, so I cannot decipher what exactly the standard says without significant effort, but you are welcome to give it a try.

However, for an informal explanation of what optimizations the compiler is allowed to do, you can take a look at the as-if rule on cppreference.

EDIT 2: the standard also formally specifies the as-if rule in [intro.execution], paragraph 7:

The least requirements on a conforming implementation are:
(7.1) — Accesses through volatile glvalues are evaluated strictly according to the rules of the abstract machine.
(7.2) — At program termination, all data written into files shall be identical to one of the possible results that execution of the program according to the abstract semantics would have produced.
(7.3) — The input and output dynamics of interactive devices shall take place in such a fashion that prompting output is actually delivered before a program waits for input. What constitutes an interactive device is implementation-defined.

In short, any optimization is valid, as long as the program produces the same output, and the reads and writes to volatile objects happen in the correct order, which holds for your original example.

like image 200
gflegar Avatar answered Oct 27 '22 10:10

gflegar