En el modo de lanzamiento, el comportamiento del código no es el esperado

131

El siguiente código genera resultados diferentes en el modo de depuración y en el modo de lanzamiento (con Visual Studio 2008):

int _tmain(int argc, _TCHAR* argv[])
{

    for( int i = 0; i < 17; i++ ) 
    { 
        int result = i * 16;

        if( result > 255 )
        {
            result = 255;
        }

        printf("i:%2d, result = %3d\n", i, result) ; 
    } 

    return 0;
}

La salida del modo de depuración, que es como se esperaba:

i: 0, result =   0
i: 1, result =  16
(...)
i:14, result = 224
i:15, result = 240
i:16, result = 255

La salida del modo de liberación, donde i: 15 resultado no es correcto:

i: 0, result =   0
i: 1, result =  16
(...)
i:14, result = 224
i:15, result = 255
i:16, result = 255

Al elegir "Optimización -> No optimizar" en Visual Studio en modo de lanzamiento, el resultado de salida será correcto. Sin embargo, me gustaría saber por qué el proceso de optimización podría conducir a resultados erróneos.


Actualizar:

Como lo sugiere Mohit JainBy, impresiones de:

printf("i:%2d, result = %3d, i*16=%d\n", i, result, i*16) ;

La salida del modo de liberación es correcta:

i: 0, result =   0, i*16=0
i: 1, result =  16, i*16=16
(...)
i:14, result = 224, i*16=224
i:15, result = 240, i*16=240
i:16, result = 255, i*16=256
Lorris Lin
fuente
15
Parece un error del compilador (y bastante significativo).
WhozCraig
1
@WhozCraig Simplemente actualiza la salida de i * 16en la publicación, y el resultado es correcto.
Lorris Lin
44
@juanchopanza: desde mi experiencia con MS y correcciones de errores a VS, corrigen dichos errores después de que se les informa sobre ellos, pero no aplican esas correcciones a versiones anteriores de VS, por lo que si por alguna razón se ve obligado a usar una versión anterior de VS, entonces uno está atrapado con tales errores hasta que se puede actualizar a una versión más nueva.
Kaiserludi
2
FWIW esto funciona bien con el próximo Visual Studio 2015
ismail

Respuestas:

115

Esto es interesante, al menos desde una perspectiva histórica. Puedo reproducir el problema con VC 2008 (15.00.30729.01) y VC 2010 (16.00.40219.01) (dirigido a 32 bits x86 o 64 bits x64). El problema no ocurre con ninguno de los compiladores que he intentado comenzar con VC 2012 (17.00.61030).

El comando que solía compilar: cl /Ox vc15-bug.cpp /FAsc

Dado que VC 2008 (y 2010) es bastante antiguo y la solución ha estado vigente durante varios años, no creo que pueda esperar ninguna acción de Microsoft, excepto el uso de un compilador más nuevo (aunque tal vez alguien pueda sugerir una solución alternativa).

El problema es que la prueba para determinar si se debe forzar el valor 255se realiza en función del recuento del bucle en lugar del resultado real de la i * 16expresión. Y el compilador simplemente obtiene el recuento incorrecto de cuándo debería comenzar a forzar el valor 255. No tengo idea de por qué sucede eso, es solo el efecto que veo:

; 6    :    for( int i = 0; i < 17; i++ ) 

  00001 33 f6        xor     esi, esi
$LL4@main:
  00003 8b c6        mov     eax, esi
  00005 c1 e0 04     shl     eax, 4

; 7    :    { 
; 8    :        int result = i * 16;
; 9    : 
; 10   :        if( result > 255 )

  // the value `esi` is compared with in the following line should be 15!
  00008 83 fe 0e     cmp     esi, 14            ; 0000000eH
  0000b 7e 05        jle     SHORT $LN1@main

; 11   :        {
; 12   :            result = 255;

  0000d b8 ff 00 00 00   mov     eax, 255       ; 000000ffH
$LN1@main:

; 13   :        }

Actualización : Todas las versiones de VC que instalé antes de VC 2008 tienen el mismo error, excepto VC6: la compilación del programa bloquea el compilador de VC6:

vc15-bug.cpp(10) : fatal error C1001: INTERNAL COMPILER ERROR

¡Entonces este es un error que duró en MSVC de una forma u otra por más de 10 años!

Michael Burr
fuente
Si mi memoria del tiempo de ensamblaje x86 es la razón correcta para la comparación de esi en lugar de eax es comp eax, 255 causaría un bloqueo de la tubería ya que se acaba de escribir eax.
Loren Pechtel
3
Mi conjetura (transformaciones): resultado> 255, resultado / 16> 255/16, i> 15, i <= 14
teki
¡Muy interesante! Además, si cambia la comparación de result > 255a, result >= 255se comporta correctamente. En VS2010 eso cambia cmp esi, 14a cmp esi, 16(y jlea jl).
opello
16

Suponiendo que sus datos informados son correctos, esto sería un error del compilador. Verifique la última versión del compilador. Si el error sigue presente, envíe un informe de error.

David Heffernan
fuente