Lun*_*din 8 c gcc volatile compiler-optimization
在编写有关编译器必须如何处理的答案时volatile,我相信我可能偶然发现了 gcc 错误,并希望有人在我报告之前进行验证。
我写了一个简单的函数,如下所示:
\nint foo (int a, int b, int c)\n{\n b = a + 1;\n c = b + 1;\n a = c + 1;\n return a;\n}\nRun Code Online (Sandbox Code Playgroud)\n如果不进行优化,这会导致大量无意义的数据来回移动。通过优化,编译器只需获取存储位置的寄存器a,然后加 3 并返回该结果。讲 x86lea eax, [rdi+3]和ret. 这是预料之中的,到目前为止一切都很好。
为了演示排序和易失性访问,我将示例更改为:
\nint foo (int a, int b, int c)\n{\n b = a + 1;\n c = *(volatile int*)&b + 1;\n a = c + 1;\n return a;\n}\nRun Code Online (Sandbox Code Playgroud)\n这里有一个对内容的左值访问,b它是 volatile 限定的,据我所知,编译器绝对不允许优化该访问1)。从 gcc 4.1.2 (可能更早)到 gcc 10.3 我得到了一致的行为(在 clang 中也是如此)。即使使用以下命令,x86 机器代码也看起来像这样-O3:
foo:\n add edi, 1\n mov DWORD PTR [rsp-4], edi\n mov eax, DWORD PTR [rsp-4]\n add eax, 2\n ret\nRun Code Online (Sandbox Code Playgroud)\n然后我在 gcc 11.1 及更高版本上尝试相同的操作,现在我得到:
\nfoo:\n lea eax, [rdi+3]\n ret\nRun Code Online (Sandbox Code Playgroud)\nhttps://godbolt.org/z/e5x74z3Kb
\nARM gcc 11.1 做了类似的事情。
\n这是编译器错误吗?
\n1)参考文献:ISO/IEC 9899:2018 5.1.2.3,特别是 \xc2\xa72、\xc2\xa74 和 \xc2\xa76。
\n将地址传递给非内联函数会使 GCC 尊重volatile稍后访问(可能更早,未检查)对函数 arg 或 local 的强制转换。 https://godbolt.org/z/cssveev7n
我复制了该行,并且由于使用 GCC 主干的易失性转换,c = asm 包含两个负载。b
void bar(void*);
int foo (int a, int b, int c)
{
bar(&b); // b's address has now "escaped" - potentially globally visible
b = a + 1;
c = *(volatile int*)&b + 1;
c = *(volatile int*)&b + 1; // both accesses present.
a = c + 1;
return a;
}
Run Code Online (Sandbox Code Playgroud)
# GCC trunk -O3 -fverbose-asm
call bar #
mov DWORD PTR [rsp+12], ebx # b, tmp89
mov eax, DWORD PTR [rsp+12] # _2, MEM[(volatile int *)&b]
mov eax, DWORD PTR [rsp+12] # _3, MEM[(volatile int *)&b]
...
add eax, 2
ret
Run Code Online (Sandbox Code Playgroud)
因此,这似乎是无辜的,除非在某些微基准用例中;它不会使用此类强制转换来破坏手动原子操作,例如Linux 内核的READ_ONCE/WRITE_ONCE宏。
仍然可以说违反了 ISO C 规则,如果int用volatile int. 如果不是,则只有 GCC 定义行为,所以这取决于 GCC。我将其发布更多是作为一个数据点,而不是关于问题的这方面的任一方向的争论。
| 归档时间: |
|
| 查看次数: |
275 次 |
| 最近记录: |