gcc -O0 aún optimiza el código no utilizado. ¿Hay un indicador de compilación para cambiar eso?

 C Programming >> Programación C >  >> Tags >> GCC
gcc -O0 aún optimiza el código no utilizado. ¿Hay un indicador de compilación para cambiar eso?


Como mencioné en esta pregunta, gcc se está eliminando (sí, con -O0 ) una línea de código _mm_div_ss(s1, s2); presumiblemente porque el resultado no se guarda. Sin embargo, esto debería desencadenar una excepción de punto flotante y generar SIGFPE, lo que no puede suceder si se elimina la llamada.


Pregunta :¿Hay una bandera, o varias banderas, para pasar a gcc para que el código se compile tal como está? Estoy pensando en algo como fno-remove-unused pero no estoy viendo nada de eso. Idealmente, esto sería un indicador del compilador en lugar de tener que cambiar mi código fuente, pero si eso no es compatible, ¿hay algún atributo/pragma gcc para usar en su lugar?


Cosas que he probado:


$ gcc --help=optimizers | grep -i remove

no hay resultados.


$ gcc --help=optimizers | grep -i unused

sin resultados.


Y deshabilitar explícitamente todos los indicadores de eliminación/código inactivo:tenga en cuenta que no hay ninguna advertencia sobre el código no utilizado:


$ gcc -O0 -msse2 -Wall -Wextra -pedantic -Winline \
-fno-dce -fno-dse -fno-tree-dce \
-fno-tree-dse -fno-tree-fre -fno-compare-elim -fno-gcse \
-fno-gcse-after-reload -fno-gcse-las -fno-rerun-cse-after-loop \
-fno-tree-builtin-call-dce -fno-tree-cselim a.c
a.c: In function ‘main’:
a.c:25:5: warning: ISO C90 forbids mixed declarations and code [-Wpedantic]
__m128 s1, s2;
^
$


Programa fuente


#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <xmmintrin.h>
static void sigaction_sfpe(int signal, siginfo_t *si, void *arg)
{
printf("%d,%d,%d\n", signal, si!=NULL?1:0, arg!=NULL?1:0);
printf("inside SIGFPE handler\nexit now.\n");
exit(1);
}
int main()
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = sigaction_sfpe;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGFPE, &sa, NULL);
_mm_setcsr(0x00001D80);
__m128 s1, s2;
s1 = _mm_set_ps(1.0, 1.0, 1.0, 1.0);
s2 = _mm_set_ps(0.0, 0.0, 0.0, 0.0);
_mm_div_ss(s1, s2);
printf("done (no error).\n");
return 0;
}

Compilar el programa anterior da


$ ./a.out
done (no error).

Cambiando la línea


_mm_div_ss(s1, s2);

a


s2 = _mm_div_ss(s1, s2); // add "s2 = "

produce el resultado esperado:


$ ./a.out
inside SIGFPE handler


Editar con más detalles.


Esto parece estar relacionado con el __always_inline__ atributo en el _mm_div_ss definición.


$ cat t.c
int
div(int b)
{
return 1/b;
}
int main()
{
div(0);
return 0;
}
$ gcc -O0 -Wall -Wextra -pedantic -Winline t.c -o t.out
$

(sin advertencias ni errores)


$ ./t.out
Floating point exception
$

vs a continuación (igual excepto por los atributos de función)


$ cat t.c
__inline int __attribute__((__always_inline__))
div(int b)
{
return 1/b;
}
int main()
{
div(0);
return 0;
}
$ gcc -O0 -Wall -Wextra -pedantic -Winline t.c -o t.out
$

(sin advertencias ni errores)


$ ./t.out
$

Agregar el atributo de función __warn_unused_result__ al menos da un mensaje útil:


$ gcc -O0 -Wall -Wextra -pedantic -Winline t.c -o t.out
t.c: In function ‘main’:
t.c:9:5: warning: ignoring return value of ‘div’, declared with attribute warn_unused_result [-Wunused-result]
div(0);
^

editar:


Algunas discusiones sobre la lista de correo de gcc. En última instancia, creo que todo funciona según lo previsto.


Respuestas:


GCC no "optimiza" nada aquí. Simplemente no genera código inútil. Parece una ilusión muy común que hay una forma pura de código que el compilador debería generar y cualquier cambio en eso es una "optimización". No existe tal cosa.


El compilador crea una estructura de datos que representa lo que significa el código, luego aplica algunas transformaciones en esa estructura de datos y, a partir de eso, genera un ensamblador que luego se compila en instrucciones. Si compila sin "optimizaciones", solo significa que el compilador solo hará el menor esfuerzo posible para generar código.


En este caso, toda la declaración es inútil porque no hace nada y se descarta de inmediato (después de expandir las líneas y lo que significan las funciones integradas, es equivalente a escribir a/b; , la diferencia es que escribir a/b; emitirá una advertencia sobre statement with no effect mientras que las funciones integradas probablemente no se manejen con las mismas advertencias). Esto no es una optimización, el compilador en realidad tendría que hacer un esfuerzo adicional para inventar el significado de una declaración sin sentido, luego falsificar una variable temporal para almacenar el resultado de esta declaración y luego desecharla.


Lo que está buscando no son banderas para deshabilitar optimizaciones, sino banderas de pesimización. No creo que ningún desarrollador de compiladores pierda el tiempo implementando tales indicadores. Aparte de tal vez como una broma del Día de los Inocentes.