为什么g ++为了简单地减少而使用movabs和一个奇怪的常数?

时间:2018-10-06 09:15:34

标签: c++ assembly g++ reduction accumulate

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

#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在实现中可能会有些棘手,但这仍然非常简单。我对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!

1 个答案:

答案 0 :(得分:6)

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的一部分。