c++assemblytranslation-unitcompiler-explorergodbolt

Where is the binary code for executing `std::vector<T>::operator[]`, when a TU calling that function is compiled with -O0?


If I compile the following with -O3

#include <vector>
int foo() {
    std::vector<int> const v{17,2,3};
    return v[0] + v[2];
}

the assembly I get is

foo():
        mov     eax, 20
        ret

(at least with Clang).

And I understand how that would work when called by some other TU.

But if I compile with -O0, I get this:

_Z3foov:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $112, %rsp
        movl    $17, -84(%rbp)
        movl    $2, -80(%rbp)
        movl    $3, -76(%rbp)
        leaq    -84(%rbp), %rax
        movq    %rax, -72(%rbp)
        movq    $3, -64(%rbp)
        leaq    -85(%rbp), %rcx
        movq    %rcx, -32(%rbp)
        movq    -32(%rbp), %rax
        movq    %rax, -8(%rbp)
        movq    -72(%rbp), %rsi
        movq    -64(%rbp), %rdx
        leaq    -56(%rbp), %rdi
        callq   _ZNSt6vectorIiSaIiEEC2ESt16initializer_listIiERKS0_
        jmp     .LBB0_1
.LBB0_1:
        leaq    -85(%rbp), %rax
        movq    %rax, -24(%rbp)
        leaq    -56(%rbp), %rdi
        xorl    %eax, %eax
        movl    %eax, %esi
        callq   _ZNKSt6vectorIiSaIiEEixEm
        movl    (%rax), %eax
        movl    %eax, -108(%rbp)
        leaq    -56(%rbp), %rdi
        movl    $2, %esi
        callq   _ZNKSt6vectorIiSaIiEEixEm
        movq    %rax, %rcx
        movl    -108(%rbp), %eax
        addl    (%rcx), %eax
        movl    %eax, -104(%rbp)
        leaq    -56(%rbp), %rdi
        callq   _ZNSt6vectorIiSaIiEED2Ev
        movl    -104(%rbp), %eax
        addq    $112, %rsp
        popq    %rbp
        retq
        movq    %rax, %rcx
        movl    %edx, %eax
        movq    %rcx, -96(%rbp)
        movl    %eax, -100(%rbp)
        leaq    -85(%rbp), %rax
        movq    %rax, -16(%rbp)
        movq    -96(%rbp), %rdi
        callq   _Unwind_Resume@PLT

__clang_call_terminate:
        pushq   %rbp
        movq    %rsp, %rbp
        callq   __cxa_begin_catch@PLT
        callq   _ZSt9terminatev@PLT

.L.str:
        .asciz  "cannot create std::vector larger than max_size()"

.L.str.1:
        .asciz  "/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/bits/stl_vector.h"

.L__PRETTY_FUNCTION__._ZNKSt6vectorIiSaIiEEixEm:
        .asciz  "const_reference std::vector<int>::operator[](size_type) const [_Tp = int, _Alloc = std::allocator<int>]"

.L.str.2:
        .asciz  "__n < this->size()"

DW.ref.__gxx_personality_v0:
        .quad   __gxx_personality_v0

where _ZNKSt6vectorIiSaIiEEixEm is the called when computing v[0] and v[2]. But I don't really understand where those 2 calls jump. I mean, the string _ZNKSt6vectorIiSaIiEEixEm appears only 3 times, 2 are the calls, and one is this:

.L__PRETTY_FUNCTION__._ZNKSt6vectorIiSaIiEEixEm:
        .asciz  "const_reference std::vector<int>::operator[](size_type) const [_Tp = int, _Alloc = std::allocator<int>]"

How can this possibly result in pulling the elemnts out of the vector?

Or, in other words, if I link the above TU with another one that calls it and returns the value, e.g.

int foo();
int main() {
  return foo();
}

where do I find the code the code that executes v[0] and v[2]? If it's not in the assembly, it's not even in the binary, right? Then how does the program run?


Solution

  • Filter: Library Functions is enabled by default in the Godbolt compiler explorer.
    Disabling that in the Filter drop-down menu, there's a definition for _ZNKSt6vectorIiSaIiEEixEm. (std::vector<int, std::allocator<int>>::operator[](unsigned long) const: with name-demangling enabled.)

    (Its definition includes bounds-checking and an assert-fail that prints the function name, hence the .L__PRETTY_FUNCTION__._ZNKSt6vectorIiSaIiEEixEm: definition which the library-function filter didn't discard).