sinónimo significado rae idioma desvirtuar desvirtuamiento desvirtualizar desvirtualizacion desvirtuación c++ gcc compiler-optimization

c++ - idioma - significado de desvirtuar rae



¿Por qué gcc no puede desvirtualizar esta función? (2)

Acabo de hacer otro experimento, como en respuesta omnifaria. Pero esta vez hago que el puntero apunte a un objeto estático:

Impl1 x; static Interface* const ptr = &x ;

GCC no desvirtualiza la llamada de función , -O2 es suficiente. No veo ninguna regla en el estándar de C ++ que haga que el puntero al almacenamiento estático se trate de manera diferente que el puntero al almacenamiento dinámico.

Se permite cambiar el objeto en la dirección ptr por ptr . Por lo tanto, el compilador debe rastrear lo que está sucediendo en esa dirección para saber cuál es el tipo dinámico real del objeto. Así que mi opinión es que el implementador del optimizador puede haber considerado que el seguimiento de lo que está sucediendo en el montón sería demasiado difícil en un programa real, por lo que el compilador simplemente no lo hace.

#include <cstdio> #include <cstdlib> struct Interface { virtual void f() = 0; }; struct Impl1: Interface { void f() override { std::puts("foo"); } }; // or __attribute__ ((visibility ("hidden")))/anonymous namespace static Interface* const ptr = new Impl1 ; int main() { ptr->f(); }

Cuando se compila con g ++ - 7 -O3 -flto -fdevirtualize-at-ltrans -fipa-pta -fuse-linker-plugin , la ptr->f() no se puede desvirtualizar.

Parece que ninguna biblioteca externa puede modificar ptr . ¿Es esta una deficiencia del optimizador de GCC, o porque algunas otras fuentes hacen que la desvirtualización no esté disponible en este caso?

Enlace de Godbolt

ACTUALIZACIÓN: Parece que clang-7 con -flto -O3 -fwhole-program-vtables -fvisibility=hidden es el único compilador + banderas (como en 2018/03) que puede desvirtualizar este programa .


Si mueve el ptr a la función principal, el resultado es muy revelador y ofrece una sugerencia sólida de por qué gcc no quiere quitar la virtualización del puntero.

El desmontaje para esto muestra que si el ''se ha inicializado la bandera estática'' es falso, inicializa la estática y luego vuelve a la llamada de la función virtual, aunque no haya pasado nada entre ella.

Esto me dice que gcc está programado para creer que cualquier tipo de puntero global persistente debe tratarse siempre como un puntero a un tipo desconocido.

De hecho, es incluso peor que esto. Si agrega una variable local, importa si la llamada a la f en el puntero estático se produce entre la creación de la variable local y la llamada a f o no. La asamblea que muestra el caso interpuesto está aquí: Otro enlace de Godbolt ; y es sencillo reorganizarlo usted mismo en el sitio para ver cómo el conjunto se convierte en una línea en línea una vez que la otra llamada no se interpone.

Por lo tanto, gcc debe asumir que el tipo real al que se refiere un puntero puede cambiar siempre que el flujo de control abandone la función por cualquier motivo. Y si se declara o no const es irrelevante. Tampoco es relevante si se toma su dirección, o cualquier otra cosa.

clang hace lo mismo. Esto me parece demasiado cauteloso, pero no soy un compilador.