Search code examples
c++gccclangcompiler-optimization

Why isn't this unused variable optimised away?


I played around with Godbolt's CompilerExplorer. I wanted to see how good certain optimizations are. My minimum working example is:

#include <vector>

int foo() {
    std::vector<int> v {1, 2, 3, 4, 5};
    return v[4];
}

The generated assembler (by clang 5.0.0, -O2 -std=c++14):

foo(): # @foo()
  push rax
  mov edi, 20
  call operator new(unsigned long)
  mov rdi, rax
  call operator delete(void*)
  mov eax, 5
  pop rcx
  ret

As one can see, clang knows the answer, but does quite a lot of stuff before returning. It seems to my that even the vector is created, because of "operator new/delete".

Can anyone explain to me what happens here and why it does not just return?

The code generated by GCC (not copied here) seems to construct the vector explicitly. Does anyone know GCC is not capable to deduce the result?


Solution

  • std::vector<T> is a fairly complicated class that involves dynamic allocation. While clang++ is sometimes able to elide heap allocations, it is a fairly tricky optimization and you should not rely on it. Example:

    int foo() {
        int* p = new int{5};
        return *p;
    }
    
    foo():                                # @foo()
            mov     eax, 5
            ret
    

    As an example, using std::array<T> (which does not dynamically allocate) produces fully-inlined code:

    #include <array>
    
    int foo() {
        std::array v{1, 2, 3, 4, 5};
        return v[4];
    }
    
    foo():                                # @foo()
            mov     eax, 5
            ret
    

    As Marc Glisse noted in the other answer's comments, this is what the Standard says in [expr.new] #10:

    An implementation is allowed to omit a call to a replaceable global allocation function ([new.delete.single], [new.delete.array]). When it does so, the storage is instead provided by the implementation or provided by extending the allocation of another new-expression. The implementation may extend the allocation of a new-expression e1 to provide storage for a new-expression e2 if the following would be true were the allocation not extended: [...]