为什么 g++ 使用 movabs 和一个奇怪的常量来进行简单的归约?
Why does g++ use movabs, and with a weird constant, for a simple reduction?
我正在编译这个简单的程序:
#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 求和,returns 求和(即 15)。
我知道 std::accumulate
在实施中可能会有一些技巧,但这仍然非常简单。不过,我对我得到的结果感到惊讶 when compiling this(在 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 bug report。谢谢@FlorianWeimer!
8589934593十六进制为0x200000001,17179869187为0x400000003。这两个 movabs
指令只是将两个 int
常量分别加载到一个 64 位寄存器中,用于初始化堆栈上的数组。您可以使用 -fno-store-merging
禁用此 GCC 优化,然后您将在 -O2
处获得类似这样的数组初始化:
movl , -40(%rsp)
…
…
movl , -36(%rsp)
…
movl , -32(%rsp)
movl , -28(%rsp)
movl , -24(%rsp)
顺便说一下,缺乏对单个常量的优化看起来像是 GCC 回归。我在 GCC 6.3 中看不到这一点。它实际上可能与商店合并有关,我认为这不是 GCC 6 的一部分。
我正在编译这个简单的程序:
#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 求和,returns 求和(即 15)。
我知道 std::accumulate
在实施中可能会有一些技巧,但这仍然非常简单。不过,我对我得到的结果感到惊讶 when compiling this(在 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 bug report。谢谢@FlorianWeimer!
8589934593十六进制为0x200000001,17179869187为0x400000003。这两个 movabs
指令只是将两个 int
常量分别加载到一个 64 位寄存器中,用于初始化堆栈上的数组。您可以使用 -fno-store-merging
禁用此 GCC 优化,然后您将在 -O2
处获得类似这样的数组初始化:
movl , -40(%rsp)
…
…
movl , -36(%rsp)
…
movl , -32(%rsp)
movl , -28(%rsp)
movl , -24(%rsp)
顺便说一下,缺乏对单个常量的优化看起来像是 GCC 回归。我在 GCC 6.3 中看不到这一点。它实际上可能与商店合并有关,我认为这不是 GCC 6 的一部分。