Memory_order在由非const左值传递时变为default

memory_order changes to default when passed by non-const lvalue

本文关键字:值传 default const order Memory      更新时间:2023-10-16
#include <atomic>
std::atomic<int> val{1};
const auto my_order = std::memory_order_relaxed; // const lvalue
int main()
{
    val.store(42, my_order);
}

这段代码没有关联,但是我注意到一些关于内存排序的奇怪之处。编译器为main (x86_64, g++ 6.2.1,使用-O3编译)生成以下程序集:

0x00000000004004c0 <+0>:     movl   $0x2a,0x200b5a(%rip)        # 0x601024 <val>
0x00000000004004ca <+10>:    xor    %eax,%eax
0x00000000004004cc <+12>:    retq

没有特殊的CPU指令来处理原子,这在x86的std::memory_order_relaxed排序中是预期的。
然而,当const限定符从my_order

中移除时,
auto my_order = std::memory_order_relaxed; // non-const lvalue

编译器生成的程序集变成:

0x00000000004004c0 <+0>:     movl   $0x2a,0x200b5a(%rip)        # 0x601024 <val>
0x00000000004004ca <+10>:    xor    %eax,%eax
0x00000000004004cc <+12>:    mfence
0x00000000004004cf <+15>:    retq

mfence指令似乎表明现在使用std::memory_order_seq_cst排序(默认)。这对我来说有点意外。即使my_order是左值(非常规地指定内存顺序),它是按值传递的(仍然是std::memory_order_relaxed),我不知道非const会如何改变结果。我在库头文件中找不到特定的重载。
对于clang,我看到了类似的结果,除了它使用xchg,这是clang表示顺序一致性的方式。如何解释这种差异呢?

作为一项规则,当编译器不能证明排序参数在编译时是已知的时,它将不会冒险并假设最坏情况。

如果my_order不是const全局变量,编译器无法知道store执行时的实际值是多少,因此它将使用std::memory_order_seq_cst。如果变量声明为const,则排序参数将生效,mfence指令将消失。