Se eu compilar o seguinte com-O3
#include <vector>
int foo() {
std::vector<int> const v{17,2,3};
return v[0] + v[2];
}
a montagem que eu recebo é
foo():
mov eax, 20
ret
( pelo menos com Clang ).
E eu entendo como isso funcionaria quando chamado por alguma outra TU.
Mas se eu compilar com -O0
, obtenho isto :
_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
onde _ZNKSt6vectorIiSaIiEEixEm
é chamado quando computando v[0]
e v[2]
. Mas eu realmente não entendo onde esses 2 call
s saltam. Quer dizer, a string _ZNKSt6vectorIiSaIiEEixEm
aparece apenas 3 vezes, 2 são o calls
, e um é este:
.L__PRETTY_FUNCTION__._ZNKSt6vectorIiSaIiEEixEm:
.asciz "const_reference std::vector<int>::operator[](size_type) const [_Tp = int, _Alloc = std::allocator<int>]"
Como isso pode resultar na retirada dos elementos do vetor?
Ou, em outras palavras, se eu vincular a TU acima com outra que a chama e retorna o valor, por exemplo
int foo();
int main() {
return foo();
}
onde encontro o código o código que executa v[0]
e v[2]
? Se não está no assembly, nem está no binário, certo? Então como o programa roda?
Filtro: Funções de Biblioteca são habilitadas por padrão no explorador do compilador Godbolt.
Desabilitando isso no menu suspenso Filtro, há uma definição para
_ZNKSt6vectorIiSaIiEEixEm
. (std::vector<int, std::allocator<int>>::operator[](unsigned long) const:
com name-demangling habilitado.)(Sua definição inclui verificação de limites e uma declaração de falha que imprime o nome da função, daí a
.L__PRETTY_FUNCTION__._ZNKSt6vectorIiSaIiEEixEm:
definição que o filtro de função de biblioteca não descartou).