2010-05-23 28 views
11

Mis preguntas están divididas en tres partesgcc optimización? ¿error? y su implicación practico para proyectar

Pregunta 1
Consideremos el siguiente código,

#include <iostream> 
using namespace std; 

int main(int argc, char *argv[]) 
{ 

    const int v = 50; 
    int i = 0X7FFFFFFF; 

    cout<<(i + v)<<endl; 

    if (i + v < i) 
    { 
     cout<<"Number is negative"<<endl; 
    } 
    else 
    { 
     cout<<"Number is positive"<<endl; 
    } 

    return 0; 
} 

No se utilizan las opciones específicas de optimización del compilador o la bandera de la junta es usado. Es el comando de compilación básico g ++ -o test main.cpp se usa para formar el ejecutable.

El código aparentemente muy simple, tiene un comportamiento extraño en SUSE 64 bit OS, gcc versión 4.1.2. La salida esperada es "Número es negativo", en cambio solo en SUSE 64 bit OS, la salida sería "Número es positivo".

Después de una cierta cantidad de análisis y haciendo un 'disass' del código, considero que el compilador optimiza en el siguiente formato -

  • Desde i es igual en ambos lados de la comparación, no se puede cambiar en la misma expresión, elimine 'i' de la ecuación.
  • Ahora, la comparación lleva a if (v < 0), donde v es una constante positiva, por lo que durante la compilación misma, la dirección de la función else cout se agrega al registro. No se pueden encontrar instrucciones cmp/jmp.

Veo que el comportamiento es solo en gcc 4.1.2 SUSE 10. Cuando se prueba en AIX 5.1/5.3 y HP IA64, el resultado es el esperado.

¿Es válida la optimización anterior?
O, ¿está utilizando el mecanismo de desbordamiento para int no es un caso de uso válido?

Pregunta 2
Ahora cuando cambio la sentencia condicional if (i + v < i)-if ((i + v) < i) incluso entonces, el comportamiento es el mismo, esto al menos yo personalmente no estar de acuerdo, ya que se proporcionan apoyos adicionales, espero que el compilador para crear una variable de tipo incorporada temporal y comparar, anula la optimización.

Pregunta 3
Supongamos que tengo una enorme base de código, un I migran mi versión del compilador, tales bug/optimización puede causar estragos en mi comportamiento del sistema. Por supuesto, desde la perspectiva empresarial, es muy ineficaz probar todas las líneas de código solo por la actualización del compilador.

Creo que para todo propósito práctico, estos tipos de error son muy difíciles de atrapar (durante la actualización) e invariablemente se filtrarán al sitio de producción.

¿Alguien puede sugerir alguna forma posible de garantizar que este tipo de error/optimización no tenga ningún impacto en mi sistema/código base existente?


PS:

  • Cuando la const para v se retira del código, a continuación, la optimización no se hace por el compilador.
  • Creo que está perfectamente bien utilizar el mecanismo de desbordamiento para determinar si la variable es de un valor MAX - 50 (en mi caso).

actualización (1)
¿Qué me gustaría lograr? variable, yo sería un contador (tipo de syncID). Si hago una operación fuera de línea (operación 50) y luego durante el inicio, me gustaría reiniciar mi contador, para esto estoy verificando el valor límite (para reiniciarlo) en lugar de agregarlo a ciegas.

No estoy seguro de si confío en la implementación del hardware. Sé que 0X7FFFFFFF es el máximo valor positivo. Todo lo que hago es agregar valor a esto, estoy esperando que el valor de retorno sea negativo. No creo que esta lógica tenga algo que ver con la implementación del hardware.

De todos modos, gracias por su aporte.


Update (2)
La mayoría de los estados INPIT que estoy confiando en el comportamiento de nivel inferior en la comprobación de desbordamiento. Tengo una pregunta con respecto a la misma,

  • Si ese es el caso, para un int sin firmar ¿cómo valido y restablezco el valor durante el desbordamiento o el desbordamiento? como si v = 10, i = 0X7FFFFFFE, quiero restablecer i = 9. ¿De manera similar para underflow?

No podría hacer eso a menos que compruebe la negatividad del número. Entonces mi afirmación es que int debe devolver un número negativo cuando se agrega un valor a + MAX_INT.

Háganme saber sus comentarios.

+2

No, no está bien, ya que este es un comportamiento indefinido. Su suposición de que un int es una cantidad de 32 bits en complemento de 2 Y que MAX_INT + algo debe ser negativo es simplemente incorrecto. – Ingo

+0

Quizás pueda decirnos qué está tratando de hacer para que podamos encontrarle una forma de hacerlo. –

+0

Buen ejemplo de por qué no puede decir: "Sé que mi procesador se adapta a un desbordamiento firmado, por lo tanto es seguro". –

Respuesta

11

Es un problema conocido, y no creo que se considere un error en el compilador. Cuando puedo compilar con gcc 4.5 con -Wall -O2 advierte

advertencia: suponiendo desbordamiento firmado no se produce cuando el supuesto de que (X + c) < X siempre es falsa

Aunque su código hace desbordamiento .

Puede pasar el indicador -fno-strict-overflow para desactivar esa optimización en particular.

+8

+1. Para enfatizar, el comportamiento de GCC se está cumpliendo porque el Estándar dice que el desbordamiento en tal operación causa un comportamiento indefinido. –

+0

"asumiendo desbordamiento firmado" - de hecho. ¿En qué estándar está prescrito, que (0x7fffffff + 50) debe desbordarse? – Ingo

+0

El estándar no requiere 0x7fffffff + 50 para desbordamiento. Sin embargo, permite que se desborde, y si lo hace, el estándar permite un comportamiento indefinido. –

0

Lo que hace la línea:

cout<<(i + v)<<endl; 

salida en el ejemplo SUSE? ¿Estás seguro de que no tienes entradas de 64 bits?

+0

Es revelador que el "error" ocurra en una implementación de 64 bits. – Ingo

+1

¿Tiene un ejemplo de un sistema con entradas de 64 bits? Creo que eso es extremadamente raro –

2

Q1: ¿Quizás el número es realmente positivo en una implementación de 64 bits? ¿Quién sabe? Antes de depurar el código, simplemente imprimía ff ("% d", i + v);

Q2: Los paréntesis están solo allí para decirle al compilador cómo analizar una expresión. Esto generalmente se realiza en forma de árbol, por lo que el optimizador no ve ningún paréntesis en absoluto. Y es libre de transformar la expresión.

Q3: Es por eso que, como programador c/C++, no debe escribir código que asume propiedades particulares del hardware subyacente, como, por ejemplo, que int es una cantidad de 32 bits en forma de complemento dos.

+0

La mayoría (¿todos?) De las máquinas de 64 bits aún definen "int" como 32 bits. Por lo tanto, un int con valor 0x7fffffff aún se desborda si lo incrementa. –

3

Su código produce un comportamiento indefinido. Los lenguajes C y C++ no tienen un "mecanismo de desbordamiento" para la aritmética de enteros con signo. Sus cálculos desbordan los enteros con signo: el comportamiento no está definido de inmediato. Teniendo en cuenta que forma la posición "un error en el compilador o no" no es diferente que intentar analizar los ejemplos i = i++ + ++i.

El compilador GCC tiene una optimización basada en esa parte de la especificación de los lenguajes C/C++. Se llama "semántica de desbordamiento estricto" o algo así. Se basa en el hecho de que agregar un valor positivo a un entero con signo en C++ siempre produce un valor mayor o resulta en un comportamiento indefinido. Esto significa inmediatamente que el compilador es perfectamente libre de asumir que la suma siempre es mayor. La naturaleza general de esa optimización es muy similar a las optimizaciones de "alias estricto" también presentes en GCC. Ambos dieron lugar a algunas quejas de las partes más "hackers" de la comunidad de usuarios de GCC, muchos de los cuales ni siquiera sospechaban que los trucos en los que confiaban en sus programas C/C++ eran simplemente pirateos ilegales.

0

Bien, entonces esto fue hace casi seis años y la pregunta es respondida. Aún así, creo que hay algunos puntos que no han sido abordados a mi satisfacción, por lo que agrego algunos comentarios, con suerte para el bien de los futuros lectores de esta discusión. (Tal como yo cuando me dieron un golpe búsqueda de ella.)

  1. El PO especifica usando gcc 4.1.2 sin ningún indicador especial. Supongo que la ausencia de la bandera -O es equivalente a -O0. Sin optimización solicitada, ¿por qué gcc optimizó el código de forma difusa? Eso me parece como un error de compilación. También asumo que esto se ha solucionado en versiones posteriores (por ejemplo, una respuesta menciona gcc 4.5 y la bandera de optimización -fno-strict-overflow). La página de manual de gcc actual indica que -fstrict-overflow se incluye con -O2 o más.

  2. En las versiones actuales de gcc, hay una opción -fwrapv que le permite usar el tipo de código que causó problemas para el OP. Siempre que, por supuesto, se asegure de conocer los tamaños de bit de sus tipos enteros. De página del manual de gcc:

 
-fstrict-overflow 
..... 
See also the -fwrapv option. Using -fwrapv means that integer signed overflow 
is fully defined: it wraps. ... With -fwrapv certain types of overflow are 
permitted. For example, if the compiler gets an overflow when doing arithmetic 
on constants, the overflowed value can still be used with -fwrapv, but not otherwise. 
Cuestiones relacionadas