¿Se permite a los compiladores eliminar los bucles infinitos?


Puede el compilador de optimización eliminar bucles infinitos, lo que no cambia ningún dato, como

while(1) 
  /* noop */;

A partir del análisis de un compilador de gráficos de flujo de datos puede derivar, que tal bucle es "código muerto" sin ningún efecto secundario.

¿Está prohibida la eliminación de bucles infinitos por los estándares C90 / C99?

¿Los estándares C90 o C99 permiten al compilador eliminar tales bucles?

Upd: "Microsoft C versión 6.0 hizo esencialmente esta optimización.", ver enlace de caf.

label: goto label;
return 0;

Será transformado a

return 0;
Author: Nietzche-jou, 2010-02-01

6 answers

C11 aclara la respuesta a esta pregunta, en la sección del borrador de la norma C116.8.5 Las declaraciones de iteración añadieron el siguiente párrafo:

Una instrucción de iteración cuya expresión controladora no es una constante expresión,156) que no realiza operaciones de entrada/salida, no acceder a objetos volátiles, y no realiza sincronización o atómica operaciones en su cuerpo, controlando la expresión, o (en el caso de para la declaración) su expresión-3, puede ser asumida por la implementación para terminar.157)

Y la nota 157 dice:

Esto está destinado a permitir transformaciones del compilador, como la eliminación de bucles vacíos, incluso cuando la terminación no puede ser probada.

Así que su ejemplo específico:

while(1) 
  /* noop */;

No es un juego justo para la optimización ya que la expresión controladora es una expresión constante.

bucles Infinitos como UB

So ¿por qué se permite a los compiladores optimizar los bucles infinitos con la excepción proporcionada anteriormente, bien Hans Boehm proporciona una justificación para hacer que los bucles infinitos sean un comportamiento indefinido en: N1528: ¿Por qué el comportamiento indefinido para los bucles infinitos?, la siguiente cita da una buena sensación para el tema involucrado: {[17]]}

Como N1509 señala correctamente, el borrador actual esencialmente da comportamiento indefinido a bucles infinitos en 6.8. 5p6. Un problema importante para hacerlo es que permite el código para moverse a través de un potencial bucle sin terminación. Por ejemplo, supongamos que tenemos los siguientes bucles, donde count y count2 son variables globales (o han tenido su dirección tomado), y p es una variable local, cuya dirección no se ha tomado:

for (p = q; p != 0; p = p -> next) {
    ++count;
}
for (p = q; p != 0; p = p -> next) {
    ++count2;
}

¿Podrían estos dos bucles ser fusionados y reemplazados por el siguiente bucle?

for (p = q; p != 0; p = p -> next) {
        ++count;
        ++count2;
}

Sin la dispensación especial en 6.8. 5p6 para bucles infinitos, esto sería desautorizado: Si el primer bucle no termina porque q apunta a una lista circular, el original nunca escribe a count2. Así se podría ejecutar en paralelo con otro subproceso que acceda o actualizaciones count2. Esto ya no es seguro con la versión transformada lo que hace acceso a count2 a pesar del bucle infinito. Así el la transformación potencialmente introduce una carrera de datos.

En casos como este, es muy poco probable que un compilador pueda para probar la terminación del bucle; tendría que entender que los puntos q a una lista acíclica, que creo que está más allá de la capacidad de la mayoría compiladores convencionales, y a menudo imposible sin todo el programa información.

C99

Desde C99 no tiene esta tallar, nos mira a la como-si la regla cubiertos en la sección 5.1.2.3 que básicamente dice que el compilador sólo tiene que emular el comportamiento observable de un programa, los requisitos son los siguientes:

Los requisitos mínimos sobre una conformidad implementation are:

  • En los puntos de secuencia, los objetos volátiles son estables en el sentido de que los accesos anteriores son los accesos completos y posteriores aún no se han producido.
  • Al finalizar el programa, todos los datos escritos en archivos serán idénticos al resultado que la ejecución del programa según la semántica abstracta habría producido.
  • La dinámica de entrada y salida de los dispositivos interactivos tendrá lugar según lo especificado en 7.19.3. Intención de estos requisitos es la salida sin búfer o con búfer de línea aparece tan pronto como sea posible, para asegurarse de que los mensajes de solicitud aparecen realmente antes de un programa esperando la entrada.

Una lectura estricta de esto parecería permitir que una implementación optimice un bucle infinito. Ciertamente podemos llegar a escenarios donde la optimización de un bucle infinito causaría un cambio en el comportamiento observable:

while(1) ;
printf( "hello world\n" ) ;

Muchos argumentarían que efectuar el la terminación de un proceso también es un comportamiento observable, esta posición se toma en Los compiladores C Refutan el Último Teorema de Fermat :

El compilador tiene una libertad considerable en la forma en que implementa el programa C, pero su salida debe tener el mismo comportamiento externamente visible que el programa tendría cuando se interpreta por la "máquina abstracta C" que se describe en el estándar. Muchas personas conocedoras (incluido yo) leen esto diciendo que el comportamiento de terminación de un programa no debe ser cambiado. Obviamente, algunos escritores de compiladores no están de acuerdo, o bien no creen que importe. El hecho de que las personas razonables no estén de acuerdo sobre la interpretación parecería indicar que el estándar C es defectuoso.

Update

De alguna manera me perdí el seguimiento del artículo anterior, Compiladores y Terminación Revisited que dice lo siguiente sobre la sección 5.1.2.3:

El segundo requisito es el complicado una. Si está hablando de la terminación del programa que se ejecuta en la máquina abstracta, entonces se cumple vacuamente porque nuestro programa no termina. Si está hablando de la terminación del programa generado por el compilador, entonces la implementación de C tiene errores porque los datos escritos en archivos (stdout es un archivo) difieren de los datos escritos por la máquina abstracta. (Esta lectura se debe a Hans Boehm; no había logrado burlarse de esta sutileza de la estándar.)

Uno también podría hacer un argumento más débil de que la necesidad de crear un carve out en C11 para permitir la eliminación de bucle vacío implica que esto no era una optimización permitida anteriormente.

¿Esto se aplica también a los infinitos bucles goto?

Creo que la intención es que esto también se aplica a infinitos bucles goto también. En C++ este es claramente el caso desde la sección 1.10 [intro.multithread] dice:

La aplicación puede supongamos que cualquier hilo eventualmente hará uno de los siguientes

  • terminar,
  • hacer una llamada a una función de E/S de biblioteca,
  • acceder o modificar un objeto volátil, o
  • realizar una operación de sincronización o una operación atómica.

Y luego la intención expresada en N1528 es que el estándar C y C++ concuerden:

Dado que los back-ends del compilador suelen ser compartidos entre compiladores de C y C++, parece que la mayoría es importante que el GT14 y el GT21 se pongan de acuerdo sobre la solución que se adopte. Las alternativas serían el tratamiento especial de los dos lenguajes por parte del back-end,o la desactivación de optimizaciones al procesar código C. Ninguno de los dos parece en absoluto deseable.

Y al final dice:

WG21 está considerando mejorar la redacción que hace que el tratamiento sea consistente. Esperemos que WG14 rastreará cualquier cambio resultante.

Actualmente, el estándar C11 no contiene la texto de la sección 5.1.2.4 Ejecuciones multiproceso y carreras de datos pero considerando N1528 parece prudente asumir que los compiladores tratarán los bucles goto infinitos como un comportamiento indefinido en C y C++.

Note también ver US comment 38 aquíy N3196 que es el documento desde el que se aplicó este cambio.

 22
Author: Shafik Yaghmour,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-11 17:01:37

No hay manera de detectar bucles infinitos universalmente: vea el Problema de Detención. Así que lo mejor que cualquier compilador podría hacer es tomar una conjetura decente-por ejemplo, el caso obvio mencionado en el OP.

Pero ¿por qué sería esto deseable? Pude ver emitiendo una advertencia y aún permitiendo el comportamiento, pero eliminar el bucle no es una "optimización" - ¡cambia el comportamiento del programa!

 9
Author: Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-01 16:19:05

El bucle no es código muerto, básicamente está impidiendo que el programa llegue a lo que viene después de él. Esto no es lo que sucedería si se eliminara el bucle, por lo que el compilador no puede eliminar el bucle.

Podría reemplazarlo con una instrucción idle dependiente de la plataforma para indicar al procesador que el subproceso no va a hacer nada más.

Lo que el compilador puede hacer es eliminar cualquier código que viene después del bucle, porque es inalcanzable y nunca lo será ejecutar.

 8
Author: Timbo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-01 16:18:20

Esto ha sido discutido muchas veces antes en comp.lang.c (eg. aquí ) sin, que yo sepa, ningún resultado de consenso.

 4
Author: caf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-02 00:17:49

Son una necesidad cuando se escriben demonios. ¿Por qué querías llamarlos código muerto?

 3
Author: dirkgently,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-01 16:14:07

Creo que los estándares más nuevos estipulan explícitamente que si una pieza de código no accede a ninguna variable volátil, realice E/S, etc. cualquier otro código que no dependa de nada calculado en la primera pieza puede ser secuenciado arbitrariamente antes de ella. Si un bucle infinito no realiza ninguna E/S, ni calcula ningún valor que se utilice más adelante en el programa, un compilador puede simplemente aplazar la ejecución del bucle hasta que todo lo demás se haya completado.

 0
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-08 05:30:40