¿Cómo eliminar el “ruido” de la salida del conjunto GCC / clang?

Quiero inspeccionar la salida del ensamblaje de aplicar boost::variant en mi código para ver qué llamadas intermedias están optimizadas.

Cuando compilo el siguiente ejemplo (con GCC 5.3 usando g++ -O3 -std=c++14 -S ), parece que el comstackdor optimiza todo y devuelve 100 directamente:

 (...) main: .LFB9320: .cfi_startproc movl $100, %eax ret .cfi_endproc (...) 

 #include  struct Foo { int get() { return 100; } }; struct Bar { int get() { return 999; } }; using Variant = boost::variant; int run(Variant v) { return boost::apply_visitor([](auto& x){return x.get();}, v); } int main() { Foo f; return run(f); } 

Sin embargo, la salida de ensamblaje completo contiene mucho más que el extracto de arriba, que para mí parece que nunca se llama. ¿Hay alguna manera de decirle a GCC / clang que elimine todo ese “ruido” y que simplemente emita lo que realmente se llama cuando se ejecuta el progtwig?


salida de ensamblaje completo:

  .file "main1.cpp" .section .rodata.str1.8,"aMS",@progbits,1 .align 8 .LC0: .string "/opt/boost/include/boost/variant/detail/forced_return.hpp" .section .rodata.str1.1,"aMS",@progbits,1 .LC1: .string "false" .section .text.unlikely._ZN5boost6detail7variant13forced_returnIvEET_v,"axG",@progbits,_ZN5boost6detail7variant13forced_returnIvEET_v,comdat .LCOLDB2: .section .text._ZN5boost6detail7variant13forced_returnIvEET_v,"axG",@progbits,_ZN5boost6detail7variant13forced_returnIvEET_v,comdat .LHOTB2: .p2align 4,,15 .weak _ZN5boost6detail7variant13forced_returnIvEET_v .type _ZN5boost6detail7variant13forced_returnIvEET_v, @function _ZN5boost6detail7variant13forced_returnIvEET_v: .LFB1197: .cfi_startproc subq $8, %rsp .cfi_def_cfa_offset 16 movl $_ZZN5boost6detail7variant13forced_returnIvEET_vE19__PRETTY_FUNCTION__, %ecx movl $49, %edx movl $.LC0, %esi movl $.LC1, %edi call __assert_fail .cfi_endproc .LFE1197: .size _ZN5boost6detail7variant13forced_returnIvEET_v, .-_ZN5boost6detail7variant13forced_returnIvEET_v .section .text.unlikely._ZN5boost6detail7variant13forced_returnIvEET_v,"axG",@progbits,_ZN5boost6detail7variant13forced_returnIvEET_v,comdat .LCOLDE2: .section .text._ZN5boost6detail7variant13forced_returnIvEET_v,"axG",@progbits,_ZN5boost6detail7variant13forced_returnIvEET_v,comdat .LHOTE2: .section .text.unlikely._ZN5boost6detail7variant13forced_returnIiEET_v,"axG",@progbits,_ZN5boost6detail7variant13forced_returnIiEET_v,comdat .LCOLDB3: .section .text._ZN5boost6detail7variant13forced_returnIiEET_v,"axG",@progbits,_ZN5boost6detail7variant13forced_returnIiEET_v,comdat .LHOTB3: .p2align 4,,15 .weak _ZN5boost6detail7variant13forced_returnIiEET_v .type _ZN5boost6detail7variant13forced_returnIiEET_v, @function _ZN5boost6detail7variant13forced_returnIiEET_v: .LFB9757: .cfi_startproc subq $8, %rsp .cfi_def_cfa_offset 16 movl $_ZZN5boost6detail7variant13forced_returnIiEET_vE19__PRETTY_FUNCTION__, %ecx movl $39, %edx movl $.LC0, %esi movl $.LC1, %edi call __assert_fail .cfi_endproc .LFE9757: .size _ZN5boost6detail7variant13forced_returnIiEET_v, .-_ZN5boost6detail7variant13forced_returnIiEET_v .section .text.unlikely._ZN5boost6detail7variant13forced_returnIiEET_v,"axG",@progbits,_ZN5boost6detail7variant13forced_returnIiEET_v,comdat .LCOLDE3: .section .text._ZN5boost6detail7variant13forced_returnIiEET_v,"axG",@progbits,_ZN5boost6detail7variant13forced_returnIiEET_v,comdat .LHOTE3: .section .text.unlikely,"ax",@progbits .LCOLDB4: .text .LHOTB4: .p2align 4,,15 .globl _Z3runN5boost7variantI3FooJ3BarEEE .type _Z3runN5boost7variantI3FooJ3BarEEE, @function _Z3runN5boost7variantI3FooJ3BarEEE: .LFB9310: .cfi_startproc subq $8, %rsp .cfi_def_cfa_offset 16 movl (%rdi), %eax cltd xorl %edx, %eax cmpl $19, %eax ja .L7 jmp *.L9(,%rax,8) .section .rodata .align 8 .align 4 .L9: .quad .L30 .quad .L10 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .quad .L7 .text .p2align 4,,10 .p2align 3 .L7: call _ZN5boost6detail7variant13forced_returnIiEET_v .p2align 4,,10 .p2align 3 .L30: movl $100, %eax .L8: addq $8, %rsp .cfi_remember_state .cfi_def_cfa_offset 8 ret .p2align 4,,10 .p2align 3 .L10: .cfi_restre_state movl $999, %eax jmp .L8 .cfi_endproc .LFE9310: .size _Z3runN5boost7variantI3FooJ3BarEEE, .-_Z3runN5boost7variantI3FooJ3BarEEE .section .text.unlikely .LCOLDE4: .text .LHOTE4: .globl _Z3runN5boost7variantI3FooI3BarEEE .set _Z3runN5boost7variantI3FooI3BarEEE,_Z3runN5boost7variantI3FooJ3BarEEE .section .text.unlikely .LCOLDB5: .section .text.startup,"ax",@progbits .LHOTB5: .p2align 4,,15 .globl main .type main, @function main: .LFB9320: .cfi_startproc movl $100, %eax ret .cfi_endproc .LFE9320: .size main, .-main .section .text.unlikely .LCOLDE5: .section .text.startup .LHOTE5: .section .rodata .align 32 .type _ZZN5boost6detail7variant13forced_returnIvEET_vE19__PRETTY_FUNCTION__, @object .size _ZZN5boost6detail7variant13forced_returnIvEET_vE19__PRETTY_FUNCTION__, 58 _ZZN5boost6detail7variant13forced_returnIvEET_vE19__PRETTY_FUNCTION__: .string "T boost::detail::variant::forced_return() [with T = void]" .align 32 .type _ZZN5boost6detail7variant13forced_returnIiEET_vE19__PRETTY_FUNCTION__, @object .size _ZZN5boost6detail7variant13forced_returnIiEET_vE19__PRETTY_FUNCTION__, 57 _ZZN5boost6detail7variant13forced_returnIiEET_vE19__PRETTY_FUNCTION__: .string "T boost::detail::variant::forced_return() [with T = int]" .ident "GCC: (Ubuntu 5.3.0-3ubuntu1~14.04) 5.3.0 20151204" .section .note.GNU-stack,"",@progbits 

.cfi directivas .cfi , las tags no utilizadas y las líneas de comentarios es un problema resuelto: los guiones detrás del comstackdor de comstackdores de Matt Godbolt son de código abierto en su proyecto github . Incluso puede resaltar el color para unir las líneas de origen a las líneas de asm (usando la información de depuración).

Puede configurarlo localmente para que pueda alimentar los archivos que forman parte de su proyecto con todas las rutas #include , etc. (utilizando -I/... ). Y para que pueda usarlo en el código fuente privado que no desea enviar a través de Internet.

La charla de Matt Godbolt CppCon2017 “¿Qué ha hecho mi comstackdor últimamente? Desatornillar la tapa del comstackdor “ muestra cómo usarlo (es bastante fácil de entender, pero tiene algunas características útiles si lee los documentos en github), y también cómo leer el asm x86 , con una introducción suave a la asm x86 para principiantes totales, y a mirar la salida del comstackdor. Continúa mostrando algunas optimizaciones claras del comstackdor (por ejemplo, para dividir por una constante), y qué tipo de funciones proporcionan una salida de asm útil para ver la salida optimizada del comstackdor (funciones args, no int a = 123; ).


Con simple gcc / clang (no g ++), -fno-asynchronous-unwind-tables evita .cfi directivas .cfi . Posiblemente también sea útil: -fno-exceptions -fno-rtti -masm=intel . Asegúrese de omitir -g .

Copie / pegue esto para uso local :

 g++ -fno-asynchronous-unwind-tables -fno-exceptions -fno-rtti -fverbose-asm \ -Wall -Wextra foo.cpp -O3 -masm=intel -S -o- | less 

Pero realmente, recomendaría simplemente usar Godbolt directamente (en línea o configurarlo localmente)! Puede cambiar rápidamente entre versiones de gcc y clang para ver si comstackdores antiguos o nuevos hacen algo tonto. (O lo que hace ICC, o incluso lo que hace MSVC). Incluso hay ARM / ARM64 gcc 6.3 y varios gcc para PowerPC, MIPS, AVR, MSP430. (Puede ser interesante ver qué sucede en una máquina donde int es más ancho que un registro, o no es de 32 bits, o en un RISC frente a x86).

Para C en lugar de C ++, use -xc -std=gnu11 o algo así; el sitio del explorador del comstackdor solo proporciona g ++ / clang ++, no gcc / clang.


Opciones útiles del comstackdor para hacer asm para el consumo humano :

  • Recuerde, su código solo tiene que comstackr, no vincular: pasar un puntero a una función externa como void ext(int*p) es una buena manera de evitar que algo se optimice . Solo necesita un prototipo para ello, sin definición, por lo que el comstackdor no puede alinearlo o hacer suposiciones sobre lo que hace.

  • Recomiendo usar -O3 -Wall -Wextra -fverbose-asm -march=haswell ) para ver el código. ( -fverbose-asm solo puede hacer que la fuente parezca ruidosa, cuando todo lo que obtienes son temporales numerados como nombres para los operandos.) Cuando estás jugueteando con la fuente para ver cómo cambia el asm, definitivamente quieres advertencias del comstackdor habilitado No quiere perder el tiempo rascándose la cabeza con el asm cuando la explicación es que hizo algo que merece una advertencia en la fuente.

  • Para ver cómo funciona la convención de llamadas, a menudo quiere mirar a la persona que llama y a quien llama sin marcar .

    Puede usar __attribute__((noinline,noclone)) foo_t foo(bar_t x) { ... } en una definición, o comstackr con gcc -O3 -fno-inline-functions -fno-inline-functions-called-once -fno-inline-small-functions para desactivar la alineación. (Pero esas opciones de línea de comando no deshabilitan la clonación de una función para propagación constante). Consulte Desde la perspectiva del comstackdor, ¿cómo se maneja la referencia para la matriz y por qué no se permite pasar por el valor (no decaer)? para un ejemplo.

    O si solo quiere ver cómo las funciones pasan / reciben args de diferentes tipos, podría usar diferentes nombres pero el mismo prototipo para que el comstackdor no tenga una definición para alinearse. Esto funciona con cualquier comstackdor.

  • -ffast-math obtendrá muchas funciones de libm en línea, algunas en una sola instrucción (especialmente con SSE4 disponible para roundsd ). Algunos se -fno-math-errno con solo -fno-math-errno , u otras partes “más seguras” de -ffast-math , sin las partes que le permiten al comstackdor redondear de manera diferente. Si tienes código FP, definitivamente míralo con / sin -ffast-math . Si no puede habilitar de forma segura ninguna de -ffast-math en su comstackción normal, tal vez tenga una idea para un cambio seguro que pueda hacer en la fuente para permitir la misma optimización sin -ffast-math .

  • -O3 -fno-tree-vectorize se optimizará sin auto-vectorización , por lo que puede obtener la optimización completa sin si desea comparar con -O2 (que no permite la autovectorización en gcc, pero sí en clang).
  • clang desenrolla bucles por defecto, por lo que -funroll-loops puede ser útil en funciones complejas . Puede tener una idea de “lo que hizo el comstackdor” sin tener que pasar por los bucles desenrollados. (gcc habilita -funroll-loops con -fprofile-use , pero no con -O3 ). (Esta es una sugerencia para código legible por humanos, no para código que se ejecutaría más rápido).
  • Definitivamente habilite algún nivel de optimización, a menos que específicamente desee saber qué hizo -O0 . Su requisito de “comportamiento de depuración predecible” hace que el comstackdor almacene / recargue todo entre cada instrucción C, de modo que puede modificar las variables C con un depurador e incluso “saltar” a una línea fuente diferente dentro de la misma función, y la ejecución continuar como si Hizo eso en la fuente C. -O0 salida es tan ruidoso con tiendas / recargas (y tan lento) no solo por falta de optimización, pero des-optimización forzada para apoyar la depuración .

Para obtener una mezcla de fuente y asm , use gcc -Wa,-adhln -c -g foo.c | less gcc -Wa,-adhln -c -g foo.c | less para pasar opciones adicionales a as . (Más discusión sobre esto en una publicación de blog y otro blog ). Tenga en cuenta que el resultado de esto no es una entrada de ensamblador válida, porque la fuente de C está allí directamente, no como un comentario de ensamblador. Entonces no lo llames un .s Un .lst podría tener sentido si desea guardarlo en un archivo.

El color de resaltado de Godbolt tiene un propósito similar, y es excelente para ayudarlo a ver cuándo las múltiples instrucciones no contiguas provienen de la misma línea de fuente. No he usado ese comando de listado de gcc, así que identifico qué tan bien lo hace, y qué fácil es de ver, en ese caso.

Me gusta la alta densidad de código del panel asm de godbolt, así que no creo que me gustaría tener líneas de origen mezcladas. Al menos no para funciones simples. Tal vez con una función que era demasiado compleja para manejar la estructura general de lo que hace el asm …


Y recuerde, cuando quiera simplemente mirar el asm, omita las constantes main() y de comstackción . Desea ver el código para tratar con una función arg en un registro, no para el código después de que la propagación constante lo convierta en return 42 , o al menos optimice algunas cosas.

Al eliminar funciones static y / o en inline de las funciones, se creará una definición autónoma para ellas, así como una definición para las personas que llamen, de modo que puede ver eso.

No coloque su código en una función llamada main() . gcc sabe que main es especial y supone que solo se llamará una vez, por lo que lo marca como “frío” y lo optimiza menos.


Otra cosa que puede hacer: si creó un main() , puede ejecutarlo y usar un depurador. stepi ( si ) pasos por instrucción. Consulte la parte inferior de la wiki de la etiqueta x86 para obtener instrucciones. Pero recuerde que el código puede optimizarse de distancia luego de ingresar en main con args en tiempo de comstackción.

__attribute__((noinline)) puede ayudar, en una función que usted no quiere incluir. gcc también hará clones de funciones de propagación constante, es decir, una versión especial con uno de los argumentos como constante, para sitios de llamadas que saben que están pasando una constante. El nombre del símbolo será .clone.foo.constprop_1234 o algo así en la salida asm. Puede usar __attribute__((noclone)) para desactivarlo también.


Por ejemplo

Si desea ver cómo el comstackdor multiplica dos enteros: puse el siguiente código en el explorador del comstackdor Godbolt para obtener el asm (de gcc -O3 -march=haswell -fverbose-asm ) de la manera incorrecta y la forma correcta de probar esta.

 // the wrong way, which people often write when they're used to creating a runnable test-case with a main() and a printf // or worse, people will actually look at the asm for such a main() int constants() { int a = 10, b = 20; return a * b; } mov eax, 200 #, ret # compiles the same as return 200; not interesting // the right way: compiler doesn't know anything about the inputs // so we get asm like what would happen when this inlines into a bigger function. int variables(int a, int b) { return a * b; } mov eax, edi # D.2345, a imul eax, esi # D.2345, b ret 

(Esta mezcla de asm y C fue hecha a mano al copiar y pegar la salida de asm de godbolt en el lugar correcto. Creo que es una buena manera de mostrar cómo una función corta comstack en las respuestas de SO / informes de errores de comstackdor / correos electrónicos).

Siempre puede mirar el ensamblaje generado desde el archivo de objeto, en lugar de usar el resultado del ensamblado de comstackdores. objdump viene a la mente.

Incluso puede decirle a objdump que mezcle la fuente con el ensamblaje, para que sea más fácil averiguar qué línea de origen corresponde a qué instrucciones. Sesión de ejemplo:

 $ cat test.cc int foo(int arg) { return arg * 42; } $ g++ -g -O3 -std=c++14 -c test.cc -o test.o && objdump -dS -M intel test.o test.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <_z3fooi>: int foo(int arg) { return arg + 1; 0: 8d 47 01 lea eax,[rdi+0x1] } 3: c3 ret 

Explicación de indicadores objdump :

  • -d desmonta todas las secciones ejecutables
  • -S ensamblado de entremezclas con la fuente ( -g requerido al comstackr con g++ )
  • -M intel elige la syntax de Intel sobre la syntax de AT & T fea ( opcional )

Me gusta insertar tags que fácilmente puedo extraer de la salida objdump.

 int main() { asm volatile ("interesting_part_begin%=:":); do_something(); asm volatile ("interesting_part_end%=:":); } 

Todavía no he tenido un problema con esto, pero el asm volatile puede ser muy difícil para el optimizador de un comstackdor porque tiende a dejar ese código intacto.