Why is this unused variable not optimized?

I played with Godbolt CompilerExplorer. I wanted to see how good certain optimizations are. My minimal working example:

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

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 

Apparently, clang knows the answer, but before returning a lot of material. It seems to me that even the vector was created due to the "new / delete operator".

Can someone explain to me what is going on here and why he didn’t just come back?

The code generated by GCC (not copied here) seems to explicitly create a vector. Does anyone know that GCC is not able to output the result?

+38
c ++ compiler-optimization gcc clang
Nov 02 '17 at 9:53 on
source share
3 answers

std::vector<T> is a rather complex class that includes dynamic allocation. Although clang++ can sometimes eliminate heap allocations , this is a rather complex 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 is not dynamically allocated) creates completely inline code :

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



As Mark Gliss noted in other comments of the answer, this is what the Standard says in [expr.new] # 10 :

Implementations are allowed to omit the call of the plug-in global distribution function ([new.delete.single], [new.delete.array]). When this is done, the storage is instead provided by the implementation or provided by expanding the allocation of another new expression. An implementation can expand the allocation of a new expression e1 to provide storage for a new expression e2 if the following were true: the distribution was not expanded: [...]

+29
Nov 02 '17 at 10:10
source share

As comments noted, operator new can be replaced. This can happen in any translation module. Optimizing a program for a case that it has not replaced requires a comprehensive analysis. And if it is replaced, you should call it, of course.

Whether the default operator new the I / O library call. This is important because the I / O library calls are observable and therefore cannot be optimized either.

+7
Nov 02 '17 at 10:11
source share

N3664 change to [expr.new], cited in one answer and one comment, allows new expressions not to call a plug-in global distribution function. But vector allocates memory using std::allocator<T>::allocate , which calls ::operator new directly, rather than through a new expression. So special permission is not applied, and, as a rule, compilers cannot exclude such direct calls ::operator new .

All hope is not lost, however for the specification std::allocator<T>::allocate this say:

Notes: the repository is obtained by calling ​::​operator new , but it is not indicated when or how often this function is called.

Using this permission, lib ++ std::allocator uses special built-in clang functions to tell the compiler that permission is allowed. With -stdlib=libc++ , clang compiles your code before

 foo(): # @foo() mov eax, 5 ret 
+4
Nov 03 '17 at 20:35
source share



All Articles