为什么 g++ 使用 movabs 和一个奇怪的常数来简单还原?



我正在编译这个简单的程序:

#include <numeric> 
int main()
{
int numbers[] = {1, 2, 3, 4, 5};
auto num_numbers = sizeof(numbers)/sizeof(numbers[0]);
return std::accumulate(numbers,  numbers + num_numbers, 0);
}

它将整数 1 到 5 相加并返回该总和(即 15(。

我意识到std::accumulate在实现中可能会有一些技巧,但这仍然非常简单。不过,我对编译这个(在 GodBolt 上(时得到的东西感到惊讶。

有了-O3,并且C++是一种面向编译时间计算的语言,我得到了预期:

main:
mov     eax, 15
ret

但是如果我深入到-O2- 仍然是一些繁重的优化 - 我不仅没有得到这个编译时计算,而且我看到了这个奇怪的程序集:

main:
movabs  rax, 8589934593
lea     rdx, [rsp-40]
mov     ecx, 1
mov     DWORD PTR [rsp-24], 5
mov     QWORD PTR [rsp-40], rax
lea     rsi, [rdx+20]
movabs  rax, 17179869187
mov     QWORD PTR [rsp-32], rax
xor     eax, eax
jmp     .L3
.L5:
mov     ecx, DWORD PTR [rdx]
.L3:
add     rdx, 4
add     eax, ecx
cmp     rdx, rsi
jne     .L5
ret

现在.L5,我得到了.L3。令人惊讶的是这些奇怪的movabs指示,往返于rax.它们是什么意思,为什么它们在那里?

PS - 我在没有-march集的x86_64上使用 GCC 8.2 进行编译。如果我添加-march=skylake--O3也会搞砸!编辑:这似乎是 GCC 中的回归,请参阅我的 GCC 错误报告。谢谢@FlorianWeimer!

8589934593以十六进制0x200000001,17179869187 0x400000003。 这两个movabs指令只是将两个int常量加载到一个 64 位寄存器中,用于初始化堆栈上的数组。 您可以使用-fno-store-merging禁用此 GCC 优化,然后您将在数组初始化-O2得到类似以下内容:

movl    $1, -40(%rsp)
…
…
movl    $2, -36(%rsp)
…
movl    $3, -32(%rsp)
movl    $4, -28(%rsp)
movl    $5, -24(%rsp)

顺便说一下,缺乏对单个常量的优化看起来像 GCC 回归。 我在 GCC 6.3 中看不到这一点。 它实际上可能与商店合并有关,我认为这不是GCC 6的一部分。

最新更新