为什么可以针对非易失性访问重新排序此易失性访问?

时间:2018-04-14 08:09:38

标签: c++ volatile non-volatile

以下代码示例来自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的副作用之后,即使B符合volatile 。但是,cppreference.com says

  

[W]在单个执行线程中,无法优化出易失性访问,或者重新排序另一个可见的副作用,即在易失性访问之后排序或排序 -

这里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

1 个答案:

答案 0 :(得分:5)

您关联的页面显示:

  

每次访问(读取或写入操作,成员函数调用等)   通过glvalue表达的volatile属性类型是   为优化目的而视为可见的副作用   (即,在单个执行线程内,易失性访问   无法优化或重新排序另一个可见的副作用   在易失性访问之前进行排序或排序。

因此,如果A不是volatile,则对A的访问不会被视为可见的副作用,而第二个语句也不适用(因为它没有#&# 39;关于重新排序可见和不可见访问的任何内容。)

编辑: 请注意,cppreference不是C ++的官方文档,而是社区工作。我确实认为粗体语句的意图是定义可见副作用是什么,但它没有写清楚。

最终引用是C ++标准(here是获取它的一些选项)。 N4659标准草稿,在 [intro.execution] ,第14段和可见副作用中定义副作用 [intro.races] 中的2页定义链。 我不是C ++标准的专家,因此我无法在没有付出巨大努力的情况下破译标准所说的内容,但欢迎您试一试。

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

编辑2:该标准还正式指定 [intro.execution] 中的 as-if规则,第7段:

  

符合实施的最低要求是:
  (7.1) - 通过不稳定的glvalues访问是严格按照   抽象机的规则。
  (7.2) - 在程序终止时,全部   写入文件的数据应与可能的数据相同   结果是根据摘要执行程序   语义会产生。
  (7.3) - 输入和输出动态   交互设备应以这样的方式进行   提示输出实际上是在程序等待之前传递的   输入。构成交互设备的是什么   实现定义的。

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