I'm compiling this simple program:
#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);
}
which sums up the integers 1 through 5 and returns that sum (i.e. 15).
I realize std::accumulate
can have a bit of trickery in the implementation, but still, this is pretty straightforward. I'm surprised by what I get when compiling this (on GodBolt), though.
With -O3
, and with C++ being a compilation-time-computation-oriented language, I get the expected:
main:
mov eax, 15
ret
but if I go down to -O2
- still some heavy optimization - not only do I not get this compile-time computation, but I see this strange piece of assembly:
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
Now .L5
and .L3
I get. The surprising thing are these strange movabs
instructions, to and from rax
. What do they mean and why are they there?
PS - I compiled using GCC 8.2 on an x86_64 with no -march
set. If I add -march=skylake
- the -O3
gets messed up too! Edit: This seems to be a regression in GCC, see my GCC bug report. Thanks @FlorianWeimer!