In modalità di rilascio, il comportamento del codice non è come previsto


131

Il codice seguente genera risultati diversi in modalità debug e modalità di rilascio (utilizzando 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;
}

L'output della modalità debug, che è come previsto:

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

L'output della modalità di rilascio, in cui il risultato i: 15 non è corretto:

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

Scegliendo "Ottimizzazione -> Non ottimizzare" in Visual Studio in modalità di rilascio, il risultato dell'output sarà corretto. Tuttavia, vorrei sapere perché il processo di ottimizzazione potrebbe portare a risultati errati.


Aggiornare:

Come suggerito da Mohit JainBy, stampe di:

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

L'output della modalità di rilascio è corretto:

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

15
Sembra un bug del compilatore (e abbastanza significativo).
WhozCraig,

1
@WhozCraig Aggiorna semplicemente l'output di i * 16nel post e il risultato è corretto.
Lorris Lin,

4
@juanchopanza: dalla mia esperienza con MS e correzioni di bug a VS risolvono tali bug dopo essere stati informati su di essi, ma non applicano tali correzioni alle versioni precedenti di VS, quindi se uno è per qualche motivo costretto a utilizzare una versione precedente di VS, quindi uno è bloccato con tali bug fino a quando non è possibile eseguire l'aggiornamento a una versione più recente.
Kaiserludi,

2
FWIW funziona perfettamente con il prossimo Visual Studio 2015
ismail il

Risposte:


115

Questo è interessante, almeno dal punto di vista storico. Posso riprodurre il problema con VC 2008 (15.00.30729.01) e VC 2010 (16.00.40219.01) (con destinazione x86 a 32 bit o x64 a 64 bit). Il problema non si verifica con nessuno dei compilatori che ho provato a partire da VC 2012 (17.00.61030).

Il comando che ho usato per compilare: cl /Ox vc15-bug.cpp /FAsc

Poiché VC 2008 (e 2010) è piuttosto vecchio e la correzione esiste da diversi anni, non penso che ci si possa aspettare alcuna azione da parte di Microsoft se non quella di utilizzare un compilatore più recente (anche se forse qualcuno può suggerire una soluzione alternativa).

Il problema è che il test per determinare se il valore deve essere forzato 255viene eseguito in base al conteggio del ciclo anziché al risultato effettivo i * 16dell'espressione. E il compilatore sbaglia semplicemente il conteggio per quando dovrebbe iniziare a forzare il valore 255. Non ho idea del perché ciò accada - è solo l'effetto che vedo:

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

Aggiornamento : tutte le versioni di VC che ho installato prima di VC 2008 hanno lo stesso bug, tranne VC6 - la compilazione del programma provoca l'arresto anomalo del compilatore VC6:

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

Quindi questo è un bug che è durato in MSVC in una forma o nell'altra per più di 10 anni!


Se la mia memoria dei tempi di assemblaggio di x86 è giusta, la ragione del confronto con esi piuttosto che con eax è comp eax, 255 causerebbe uno stallo della pipeline dato che eax è appena stato scritto.
Loren Pechtel,

3
La mia ipotesi (trasformazioni): risultato> 255, risultato / 16> 255/16, i> 15, i <= 14
teki

Molto interessante! Anche se si modifica il confronto da result > 255a result >= 255si comporta correttamente. In VS2010 questo cambia cmp esi, 14in cmp esi, 16(e in jlea jl).
opello,

16

Supponendo che i fatti riportati siano corretti, si tratterebbe di un bug del compilatore. Controlla l'ultima versione del compilatore. Se il bug è ancora presente, inviare una segnalazione di bug.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.