para optimizada c++ gcc clang compiler-optimization

c++ - optimizada - ¿Por qué no se optimiza esta variable no utilizada?



seo url (3)

Jugué con Godbolt''s CompilerExplorer. Quería ver qué buenas son ciertas optimizaciones. Mi ejemplo mínimo de trabajo es:

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

El ensamblador generado (por 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

Como se puede ver, clang sabe la respuesta, pero hace un montón de cosas antes de regresar. Me parece que incluso el vector se crea, debido a "operador nuevo / eliminar".

¿Alguien puede explicarme qué pasa aquí y por qué no solo regresa?

El código generado por GCC (no copiado aquí) parece construir el vector explícitamente. ¿Alguien sabe que GCC no es capaz de deducir el resultado?


Como se nota en los comentarios, el operator new puede ser reemplazado. Esto puede suceder en cualquier Unidad de Traducción. La optimización de un programa para el caso de que no se remplace requiere, por lo tanto, un análisis de todo el programa. Y si es reemplazado, tienes que llamarlo por supuesto.

Si el operator new predeterminado operator new es una llamada de E / S de la biblioteca no está especificado. Eso es importante, porque las llamadas de E / S de la biblioteca son observables y, por lo tanto, tampoco pueden optimizarse.


El cambio de N3664 a [expr.new], citado en una respuesta y un comentario, permite que las nuevas expresiones no invoquen una función de asignación global reemplazable. Pero vector asigna memoria usando std::allocator<T>::allocate , que llama ::operator new directamente, no a través de una nueva expresión . De modo que ese permiso especial no se aplica, y generalmente los compiladores no pueden eludir tales llamadas directas a ::operator new .

Sin embargo, no se pierde la esperanza de que la especificación std::allocator<T>::allocate tenga this que decir:

Observaciones: el almacenamiento se obtiene llamando al ​::​operator new , pero no se especifica cuándo o con qué frecuencia se llama a esta función.

Aprovechando este permiso, el std::allocator libc ++ utiliza incorporaciones de clang especiales para indicar al compilador que la elisión está permitida. Con -stdlib=libc++ , clang compila tu código a

foo(): # @foo() mov eax, 5 ret


std::vector<T> es una clase bastante complicada que implica asignación dinámica. Si bien clang++ veces puede elide las asignaciones de heap , es una optimización bastante complicada y no debe confiar en ella. Ejemplo:

int foo() { int* p = new int{5}; return *p; }

foo(): # @foo() mov eax, 5 ret

Como ejemplo, usar std::array<T> (que no asigna dinámicamente) produce un código completamente en línea :

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

foo(): # @foo() mov eax, 5 ret

Como Marc Glisse notó en los comentarios de la otra respuesta, esto es lo que dice el Estándar en [expr.new] # 10 :

Una implementación puede omitir una llamada a una función de asignación global reemplazable ([new.delete.single], [new.delete.array]). Cuando lo hace, el almacenamiento lo proporciona la implementación o se proporciona extendiendo la asignación de otra nueva expresión. La implementación puede extender la asignación de una nueva expresión e1 para proporcionar almacenamiento para una nueva expresión e2 si lo siguiente sería cierto si la asignación no se extendiera: [...]