为什么可以相对于非易失性访问对这种易失性访问进行重新排序?

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

下面的代码示例来自Chinese blog,其中介绍了volatile的效果。左边是C代码;另一个是生成的汇编代码。

// 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

正如我们在汇编代码中看到的,A 的副作用放在 B 的副作用之后,即使 Bvolatile 合格的。但是,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.

这里A的副作用排在B之前,所以我认为编译器这样做是不合法的。我说得对吗?


作为补充,博客说如果我们要保证volatilenon-volatile类型之间的顺序,我们需要同时使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

您链接的页面显示:

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.

因此,如果 A 不是 volatile,对 A 的访问不会被视为可见的副作用,并且第二个语句不适用(因为它不不要说任何关于重新排序可见和不可见访问的事情。

编辑: 请注意,cppreference 不是 C++ 的官方文档,而是社区的成果。我确实相信粗体声明的目的是定义什么是可见的副作用,但没有写清楚。

最终参考是 C++ 标准(here 是获取它的一些选项)。 N4659 标准草案在 [intro.execution] 第 14 段和 [=32= 中定义了 副作用 ]可见的副作用 通过 [intro.races] 中的 2 页定义链。 我不是 C++ 标准的专家,所以我无法不费吹灰之力就理解标准的确切内容,但欢迎您尝试一下。

但是,对于允许编译器进行哪些优化的非正式解释,您可以查看 cppreference 上的 as-if rule

编辑 2:该标准还在 [intro.execution] 中正式指定了 as-if 规则 ,第 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.

简而言之,任何优化都是有效的,只要程序产生相同的输出,并且对 volatile 对象的读取和写入以正确的顺序发生,这适用于您的原始示例。