clang vs gcc:不同的易失性访问代码

Nor*_*ame 7 c++ assembly gcc volatile clang

考虑这个例子:

volatile unsigned int x;
unsigned int y;

void f() {
    x /= 2;
}
void g() {
    y /= 2;
}
Run Code Online (Sandbox Code Playgroud)

当使用-Os编译时,clang-6.0在x64上为f和g生成相同的shrl <offset>(%rip)指令模式(参见https://godbolt.org/g/hUPprL),而gcc-7.3生成此参数(请参阅https:// godbolt. org/g/vMcKVV)对于f():

 mov 0x200b67(%rip),%eax # 601034 <x>
 shr %eax
 mov %eax,0x200b5f(%rip) # 601034 <x>
Run Code Online (Sandbox Code Playgroud)

这只是一个错过的优化,还是有理由让gcc shrl <offset>(%rip)在易失性访问时拒绝?谁错了?

Pau*_*ulR 4

这只是 gcc 错过的优化。两种实现都精确地保留了读取和写入x,因此都是正确的。

对内存操作数进行“幕后操作”执行与较长实现相同的加载和存储。

  • 是的,gcc 不喜欢将“易失性”加载/存储折叠到其他操作中。在这种情况下,根据 [Agner Fog 的测试](http://agner.org/optimize),这也是一个错过的性能优化:Sandybridge-family 将 `shr [mem], imm` 作为 3 个融合域 uops 运行,相同作为单独的加载/shr/存储,更紧凑的代码通常对于 L1i 缓存密度和 uop 缓存密度更好。(Agner 的 Haswell/Skylake 表似乎是错误的;它仍然只有 1 个 ALU uop 用于端口 0 或 6 用于内存目标移位。他说“2p06 p237 p4”,但我在 SKL 上进行了测试,它实际上是“p06 p23 p237 p4” ) (2认同)