"Instrucción de hardware ilegal" de código muy simple

9

Mientras investigaba un reclamo dudoso , escribí este pequeño programa de pruebanoway.c

int proveit()
{
    unsigned int n = 0;
    while (1) n++;
    return 0;
}

int main()
{
    proveit();
    return 0;
}

Al probar esto, obtengo:

$ clang -O noway.c
$ ./a.out
zsh: illegal hardware instruction  ./a.out

Wat

Si compilo sin optimizaciones, se cuelga como se esperaba. Miré el ensamblaje, y sin todas las campanas y silbatos, la mainfunción se ve así:

_main:                                  ## @main
    pushq   %rbp
    movq    %rsp, %rbp
    ud2

Donde ud2aparentemente es una instrucción específica para el comportamiento indefinido. La afirmación dudosa antes mencionada, "Una función que nunca regresa es UB", se refuerza. Sin embargo, todavía me cuesta creerlo. ¿¡De Verdad!? ¿No puedes escribir un loop de giro de forma segura?

Entonces supongo que mis preguntas son:

  1. ¿Es esta una lectura correcta de lo que está pasando?
  2. Si es así, ¿alguien puede señalarme algún recurso oficial que lo verifique?
  3. ¿En qué situación desearía que se produjera este tipo de optimización?

Información relevante

$ clang --version
Apple clang version 11.0.0 (clang-1100.0.20.17)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
luqui
fuente
3
IIRC, el desbordamiento int firmado es UB.
wildplasser
1
^^^^ ie int n = 0===> unsigned int n = 0;o mejor aún ..while (1);
WhozCraig
1
Hmmm ... Compiler Explorer clang obtiene una instrucción de salto de auto-objetivo con -O gcc.godbolt.org/z/NTCQYT y una traducción línea por línea sin ella. Parece consistente en muchas versiones. Pero también recuerdo (aunque no lo buscaré) que el estándar C dice que una no terminación es ub si está libre de efectos secundarios . Aquí está Hans Boehm explicando que esto permite ciertas optimizaciones del compilador que de otra manera serían imposibles: open-std.org/jtc1/sc22/wg14/www/docs/n1528.htm
Gene
1
El comportamiento indefinido es el desbordamiento de entero con signo, no el bucle infinito. Puede solucionarlo usandounsigned int
MM
2
@ Gene No creo que diga eso. Se puede suponer que los bucles sin efectos secundarios con expresiones de control sin constante terminan ( port70.net/~nsz/c/c11/n1570.html#6.8.5p6 ) pero el bucle ocupado debería estar bien. El UB está en el desbordamiento de enteros, aunque no puedo reproducir el ejemplo con la instrucción UD.
PSkocik

Respuestas:

3

Si obtiene el ud2 para el código que ahora está en la pregunta, entonces el compilador no es un compilador de C conforme. Podría informar un error del compilador.

Tenga en cuenta que en C ++ este código en realidad sería UB. Cuando se agregaron subprocesos (C11 y C ++ 11 respectivamente), se estableció una garantía de progreso hacia adelante para cualquier subproceso, incluido el subproceso de ejecución principal de un programa que no tiene subprocesos múltiples.

En C ++, todos los hilos deben progresar eventualmente, sin excepciones. Sin embargo, en C, un ciclo cuya expresión de control es una expresión constante no es necesario para progresar. Tengo entendido que C agregó esta excepción porque ya era una práctica común en la codificación incrustada usar a while(1) {}para colgar el hilo.

Pregunta similar con respuestas más detalladas

MM
fuente
Sí, obtengo el ud2código C, pero gracias por incluir la información sobre la garantía de avance de C ++ (un término que parece que podré investigar), que es lo que realmente estaba preguntando, pero me optimicé. Estaba preparando la pregunta.
luqui
Una mejor redacción de lo que creo que el Comité de Estándares estaba tratando de decir es que si diferir todo dentro de un ciclo pasado una determinada operación no afectaría de manera observable el comportamiento del programa, diferir la ejecución del ciclo como un todo pasado esa operación no se consideraría un cambio observable de comportamiento.
supercat