W trybie wydania zachowanie kodu nie jest zgodne z oczekiwaniami

131

Poniższy kod generuje różne wyniki w trybie debugowania i trybie wydania (przy użyciu programu 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;
}

Dane wyjściowe trybu debugowania, zgodnie z oczekiwaniami:

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

Wyjście trybu zwolnienia, gdzie wynik i: 15 jest nieprawidłowy:

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

Po wybraniu opcji „Optymalizacja -> Nie optymalizować” w programie Visual Studio w trybie wydania, wynik wyjściowy będzie poprawny. Chciałbym jednak wiedzieć, dlaczego proces optymalizacji może prowadzić do błędnych wyników.


Aktualizacja:

Jak sugeruje Mohit JainBy, drukuje:

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

Wyjście trybu zwolnienia jest poprawne:

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
źródło
15
To wygląda na błąd kompilatora (i to dość znaczący).
WhozCraig,
1
@WhozCraig Po prostu aktualizuje dane wyjściowe i * 16w poście, a wynik jest poprawny.
Lorris Lin,
4
@juanchopanza: Z moich doświadczeń z MS i poprawkami błędów do VS, naprawiają takie błędy po tym, jak zostali poinformowani o nich, ale nie stosują tych poprawek do starszych wersji VS, więc jeśli ktoś jest z jakiegoś powodu zmuszony do korzystania ze starszej wersji VS, to utknęliśmy z takimi błędami, dopóki nie można zaktualizować do nowszej wersji.
Kaiserludi
2
FWIW Działa to dobrze z nadchodzącym Visual Studio 2015
ismail

Odpowiedzi:

115

To ciekawe, przynajmniej z historycznego punktu widzenia. Mogę odtworzyć problem z VC 2008 (15.00.30729.01) i VC 2010 (16.00.40219.01) (ukierunkowany na 32-bitowe x86 lub 64-bitowe x64). Problem nie występuje w przypadku żadnego z kompilatorów, które próbowałem, począwszy od VC 2012 (17.00.61030).

Polecenie, którego użyłem do skompilowania: cl /Ox vc15-bug.cpp /FAsc

Ponieważ VC 2008 (i 2010) jest dość stary, a poprawka jest już dostępna od kilku lat, nie sądzę, aby można było oczekiwać od Microsoftu jakiejkolwiek akcji poza użyciem nowszego kompilatora (choć może ktoś może zasugerować obejście tego problemu).

Problem polega na tym, że test określający, czy wartość powinna zostać wymuszona, 255jest wykonywany na podstawie liczby pętli, a nie faktycznego wyniku i * 16wyrażenia. A kompilator po prostu błędnie oblicza, kiedy powinien zacząć wymuszać wartość 255. Nie mam pojęcia, dlaczego tak się dzieje - to tylko efekt, który widzę:

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

Aktualizacja : Wszystkie wersje VC, które zainstalowałem wcześniej niż VC 2008 mają ten sam błąd, z wyjątkiem VC6 - kompilacja programu powoduje awarię kompilatora VC6:

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

Więc to jest błąd, który trwał w MSVC w takiej czy innej formie przez ponad 10 lat!

Michael Burr
źródło
Jeśli moja pamięć czasu montażu x86 jest właściwa, powodem porównania do esi zamiast eax jest comp eax, 255 spowodowałoby zatrzymanie potoku, ponieważ eax właśnie został napisany.
Loren Pechtel,
3
Moje przypuszczenie (transformacje): wynik> 255, wynik / 16> 255/16, i> 15, i <= 14
teki
Bardzo interesujące! Również jeśli zmienisz porównanie z result > 255na result >= 255, zachowuje się poprawnie. W VS2010 to zmienia się cmp esi, 14na cmp esi, 16(i jleto jl).
opello 17.07.15
16

Zakładając, że zgłoszone fakty są poprawne, byłby to błąd kompilatora. Sprawdź najnowszą wersję kompilatora. Jeśli błąd nadal występuje, prześlij raport o błędzie.

David Heffernan
źródło