memory_order 通过非常量左值传递时更改为默认值

memory_order changes to default when passed by non-const lvalue

#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   [=12=]x2a,0x200b5a(%rip)        # 0x601024 <val>
0x00000000004004ca <+10>:    xor    %eax,%eax
0x00000000004004cc <+12>:    retq

没有特殊的 CPU 指令来处理 x86 上预期的 std::memory_order_relaxed 排序的原子。
但是,当从 my_order

中删除 const 限定符时
auto my_order = std::memory_order_relaxed; // non-const lvalue

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

0x00000000004004c0 <+0>:     movl   [=14=]x2a,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指令消失。