Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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).

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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).

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading