2015-07-09 77 views
130

Il seguente codice genera diversi risultati sotto la modalità di debug e modalità di rilascio (utilizzando Visual Studio 2008):In modalità di rilascio, il comportamento del codice non è come previsto

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'uscita di modalità di 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'uscita della modalità di rilascio, dove i: 15 risultato 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 di output sarà corretto. Tuttavia mi piacerebbe sapere perché il processo di ottimizzazione potrebbe portare a output errati.


Aggiornamento:

Come suggerito da Mohit JainBy, stampe:

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

L'uscita 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

Questo sembra un bug del compilatore (e piuttosto significativo). – WhozCraig

+1

@WhozCraig Aggiunge semplicemente l'output di 'i * 16' nel post e il risultato è corretto. –

+4

@juanchopanza: Dalla mia esperienza con MS e correzioni di errori su VS, risolvono tali bug dopo che li hanno informati, ma non applicano quelle correzioni alle versioni precedenti di VS, quindi se uno è costretto per qualche motivo a usare un vecchio versione di VS, quindi uno è bloccato con tali bug fino a quando non è possibile eseguire l'aggiornamento a una versione più recente. – Kaiserludi

risposta

114

Questo è interessante, almeno da una prospettiva storica. Posso riprodurre il problema con VC 2008 (15.00.30729.01) e VC 2010 (16.00.40219.01) (con targeting x86 a 32 bit o 64 bit x64). 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

Dal VC 2008 (e 2010) è piuttosto vecchio e la correzione è stato in per diversi anni, non credo che ci si può aspettare qualsiasi azione da Microsoft ad eccezione 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 su 255 viene eseguito in base al numero di cicli piuttosto che al risultato effettivo dell'espressione i * 16. E il compilatore ottiene semplicemente il conteggio sbagliato per quando dovrebbe iniziare a forzare il valore a 255. Non ho idea del perché ciò accade - è solo l'effetto che vedo:

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

    00001 33 f6  xor  esi, esi 
[email protected]: 
    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 [email protected] 

; 11 :  { 
; 12 :   result = 255; 

    0000d b8 ff 00 00 00 mov  eax, 255  ; 000000ffH 
[email protected]: 

; 13 :  } 

Aggiornamento: Tutte le versioni di VC che ho installato prima di VC 2008 è lo stesso problema, tranne VC6 - compilare il programma si blocca il compilatore VC6:

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

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

+0

Se la mia memoria dei tempi di assemblaggio x86 è corretta, il motivo per il confronto con esi piuttosto che con eax è comp eax, 255 causerebbe uno stallo della pipeline appena Eix è stato appena scritto. –

+3

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

+0

Molto interessante! Inoltre se cambi il confronto da 'risultato> 255' a' risultato> = 255' si comporta correttamente. In VS2010 questo cambia 'cmp esi, 14' in' cmp esi, 16' (e 'jle' in' jl'). – opello

16

Assumendo che il fatti riportati sono corretto, questo sarebbe un bug del compilatore. Controlla l'ultima versione del compilatore. Se il bug è ancora presente, invia un bug report.