2

I was trying to figure out differences between gcc and clang and how they handle std::vector. Can anyone with more knowledge explain why gcc and clang produce such different output?

gcc 9.2: https://godbolt.org/z/AFN46d clang 9.0: https://godbolt.org/z/kEkpWE

The program:

#include <vector>

int foo(int a) {
    auto vec = std::vector<int>{};
    vec.push_back(1);
    vec.push_back(2);
    return vec[1] * a;
}
int main () {
    return foo(5) + foo(4);
}

clang produces pretty understandable assembly. However, gcc produces this weird stuff:

void std::vector<int, std::allocator<int> >::_M_realloc_insert<int>(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int&&):
        movabs  rcx, 2305843009213693951
        push    r15
        push    r14
        push    r13
        push    r12
        push    rbp
        push    rbx
        sub     rsp, 24
        mov     r12, QWORD PTR [rdi+8]
        mov     r8, QWORD PTR [rdi]
        mov     rax, r12
        sub     rax, r8
        sar     rax, 2
        cmp     rax, rcx
        je      .L16
        mov     r15, rdx
        mov     rdx, rsi
        mov     rbp, rdi
        mov     r13, rsi
        sub     rdx, r8
        test    rax, rax
        je      .L11
        movabs  r14, 9223372036854775804
        lea     rsi, [rax+rax]
        cmp     rax, rsi
        jbe     .L17

Where are these magic numbers coming from?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
therealtypon
  • 455
  • 2
  • 5
  • 12
  • I wonder why neither of them optimizes to `return 2 * a;` – M.M Feb 16 '20 at 02:09
  • 2
    The code for `foo(int)` is introduced by the line `foo(int):`, you are quoting the assembly for a vector provate member function – M.M Feb 16 '20 at 02:12
  • @M.M I suppose it is because `::operator new` may be replaced in another translation unit. – L. F. Feb 16 '20 at 04:23
  • @L.F. It has been allowed to optimize out even if global replacement exists, since C++14, [see here](https://stackoverflow.com/questions/31873616/is-the-compiler-allowed-to-optimize-out-heap-memory-allocations) – M.M Feb 16 '20 at 06:29

1 Answers1

2

GCC tries to be on the safe side and emits some additional checks. The first constant you're seeing is merely

__gnu_cxx::__numeric_traits<ptrdiff_t>::__max / sizeof(int)

and constitutes the largest possible size of vector of ints on x86-64.

The second constant appears to be just

__gnu_cxx::__numeric_traits<ptrdiff_t>::__max

rounded down to be divisible by sizeof(int).

In general, those things come from various max_size() methods in libstdc++. It is possible that clang is capable of proving some checks unnecessary.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
oakad
  • 6,945
  • 1
  • 22
  • 31