https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86276
Bug ID: 86276 Summary: Poor codegen when returning a std::vector Product: gcc Version: 9.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: redbeard0531 at gmail dot com Target Milestone: --- https://godbolt.org/g/aCiuAy While I'm a bit sad that good() isn't just "ret", I think that the current rules for allocation elision require something like that codegen. I'd expect bad() to codegen to roughly the same as good(), but then rather than calling operator delete, it should store [rax, rax + 1, rax + 22] to the three qwords starting at the out pointer. Instead, it does things like checking if the vector pointer it just zeroed is still zero to see if it needs to be deleted. It also seems to register an exception landing pad (I'm guessing to cover the operator new call) to handle freeing the vector's memory, even though it should know it isn't holding any. If I had to guess, I'd say the problem is that it thinks the hidden return out pointer has escaped when it hasn't really until a successful return. I'm pretty sure nothing in the language allows any way to access the return value before a function returns like that, but I'm now really curious if I'm wrong. If there is, is there any way to tell gcc that I promise I'm not doing anything quite that stupid? PS- is it helpful to include the code and asm here in addition to the godbolt links? #include <vector> #include <cstdint> auto good() { std::vector<uint8_t> something; something.reserve(22); something = {0x02}; //return something; Only difference from bad } auto bad() { std::vector<uint8_t> something; something.reserve(22); something = {0x02}; return something; } good(): sub rsp, 8 mov edi, 22 call operator new(unsigned long) mov BYTE PTR [rax], 2 mov rdi, rax add rsp, 8 jmp operator delete(void*) bad(): push rbp pxor xmm0, xmm0 push rbx mov rbx, rdi sub rsp, 24 mov QWORD PTR [rdi+16], 0 movups XMMWORD PTR [rdi], xmm0 mov edi, 22 call operator new(unsigned long) mov rdi, QWORD PTR [rbx] test rdi, rdi je .L5 mov QWORD PTR [rsp+8], rax call operator delete(void*) mov rax, QWORD PTR [rsp+8] .L5: mov BYTE PTR [rax], 2 lea rdx, [rax+22] mov QWORD PTR [rbx], rax add rax, 1 mov QWORD PTR [rbx+8], rax mov rax, rbx mov QWORD PTR [rbx+16], rdx add rsp, 24 pop rbx pop rbp ret mov rbp, rax jmp .L6 bad() [clone .cold.25]: .L6: mov rdi, QWORD PTR [rbx] test rdi, rdi je .L7 call operator delete(void*) .L7: mov rdi, rbp call _Unwind_Resume