En mode version, le comportement du code n'est pas comme prévu

131

Le code suivant génère différents résultats en mode débogage et en mode version (à l'aide de 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 sortie du mode débogage, qui est comme prévu:

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

La sortie du mode de libération, où le résultat i: 15 n'est pas correct:

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

En choisissant «Optimisation -> Ne pas optimiser» dans Visual Studio en mode version, le résultat de sortie sera correct. Cependant, j'aimerais savoir pourquoi le processus d'optimisation peut conduire à une sortie erronée.


Mettre à jour:

Comme suggéré par Mohit JainBy, imprime par:

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

La sortie du mode de déclenchement est correcte:

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
la source
15
Cela ressemble à un bogue du compilateur (et assez important à cela).
WhozCraig
1
@WhozCraig Met simplement à jour la sortie de i * 16dans le message, et le résultat est correct.
Lorris Lin
4
@juanchopanza: D'après mon expérience avec MS et les corrections de bogues à VS, ils corrigent ces bogues après en avoir été informés, mais n'appliquez pas ces correctifs aux anciennes versions de VS, donc si l'on est pour une raison quelconque obligé d'utiliser une version plus ancienne de VS, alors on est coincé avec de tels bogues jusqu'à ce que l'on puisse mettre à niveau vers une version plus récente.
Kaiserludi
2
FWIW cela fonctionne bien avec le prochain Visual Studio 2015
ismail

Réponses:

115

C'est intéressant, du moins d'un point de vue historique. Je peux reproduire le problème avec VC 2008 (15.00.30729.01) et VC 2010 (16.00.40219.01) (ciblant soit 32 bits x86 ou 64 bits x64). Le problème ne se produit avec aucun des compilateurs que j'ai essayés à partir de VC 2012 (17.00.61030).

La commande que j'ai utilisée pour compiler: cl /Ox vc15-bug.cpp /FAsc

Étant donné que VC 2008 (et 2010) est plutôt ancien et que le correctif existe depuis plusieurs années maintenant, je ne pense pas que vous puissiez vous attendre à une action de Microsoft sauf à utiliser un compilateur plus récent (bien que quelqu'un puisse peut-être suggérer une solution de contournement).

Le problème est que le test pour déterminer si la valeur doit être forcée 255est effectué en fonction du nombre de boucles plutôt que du résultat réel de l' i * 16expression. Et le compilateur obtient simplement le nombre erroné du moment où il doit commencer à forcer la valeur à 255. Je n'ai aucune idée de pourquoi cela se produit - c'est juste l'effet que je vois:

; 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   :        }

Mise à jour : Toutes les versions de VC que j'ai installées avant VC 2008 ont le même bogue, sauf VC6 - la compilation du programme plante le compilateur VC6:

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

C'est donc un bug qui a duré dans MSVC sous une forme ou une autre pendant plus de 10 ans!

Michael Burr
la source
Si ma mémoire de la synchronisation de l'assemblage x86 est correcte, la raison de la comparaison à esi plutôt qu'à eax est comp eax, 255 provoquerait un blocage du pipeline car eax vient d'être écrit.
Loren Pechtel
3
Ma supposition (transformations): résultat> 255, résultat / 16> 255/16, i> 15, i <= 14
teki
Très intéressant! De plus, si vous modifiez la comparaison de result > 255à, result >= 255elle se comporte correctement. Dans VS2010, cela change cmp esi, 14en cmp esi, 16(et jleen jl).
opello le
16

En supposant que vos faits rapportés sont corrects, ce serait un bogue du compilateur. Vérifiez la dernière version du compilateur. Si le bogue est toujours présent, soumettez un rapport de bogue.

David Heffernan
la source