No modo de liberação, o comportamento do código não é o esperado

131

O código a seguir gera resultados diferentes nos modos de depuração e versão (usando o 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;
}

A saída do modo de depuração, conforme o esperado:

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

A saída do modo de liberação, onde o resultado i: 15 não está correto:

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

Ao escolher "Otimização -> Não otimizar" no Visual Studio no modo de liberação, o resultado da saída estará correto. No entanto, gostaria de saber por que o processo de otimização pode levar a resultados errados.


Atualizar:

Como sugerido por Mohit JainBy, imprime por:

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

A saída do modo de liberação está correta:

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
fonte
15
Parece um bug do compilador (e bastante significativo nisso).
WhozCraig
1
@WhozCraig Apenas atualiza a saída de i * 16no post e o resultado está correto.
Lorris Lin
4
@juanchopanza: Da minha experiência com o MS e as correções para o VS, eles corrigem esses erros depois de serem informados sobre eles, mas não os aplicam em versões mais antigas do VS, por isso, se por algum motivo for forçado a usar uma versão mais antiga do VS VS, então um fica preso com esses bugs até que você possa atualizar para uma versão mais recente.
Kaiserludi
2
FWIW isso funciona bem com o Visual Studio 2015
ismail

Respostas:

115

Isso é interessante, pelo menos de uma perspectiva histórica. Posso reproduzir o problema com o VC 2008 (15.00.30729.01) e o VC 2010 (16.00.40219.01) (segmentando x86 de 32 bits ou x64 de 64 bits). O problema não ocorre com nenhum dos compiladores que tentei iniciar no VC 2012 (17.00.61030).

O comando que eu usei para compilar: cl /Ox vc15-bug.cpp /FAsc

Como o VC 2008 (e 2010) é bastante antigo e a correção já existe há vários anos, acho que você não pode esperar nenhuma ação da Microsoft, exceto o uso de um compilador mais recente (embora talvez alguém possa sugerir uma solução alternativa).

O problema é que o teste para determinar se o valor deve ser forçado 255é feito com base na contagem de loop, e não no resultado real da i * 16expressão. E o compilador simplesmente errou a contagem para quando deveria começar a forçar o valor 255. Não tenho idéia do por que isso acontece - é apenas o efeito que vejo:

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

Atualização : Todas as versões do VC que instalei anteriormente ao VC 2008 têm o mesmo bug, exceto o VC6 - a compilação do programa trava o compilador VC6:

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

Portanto, esse é um bug que durou no MSVC de uma forma ou de outra por mais de 10 anos!

Michael Burr
fonte
Se minha memória do tempo de montagem do x86 for correta, o motivo da comparação com esi em vez de eax é comp eax, 255 causaria uma paralisação do pipeline, já que o eax acabou de ser gravado.
Loren Pechtel 10/07
3
Meu palpite (transformações): resultado> 255, resultado / 16> 255/16, i> 15, i <= 14
teki
Muito interessante! Além disso, se você alterar a comparação de result > 255para result >= 255ela se comporta corretamente. No VS2010, isso muda cmp esi, 14para cmp esi, 16(e jlepara jl).
Opello 17/07/2015
16

Supondo que seus fatos relatados estejam corretos, isso seria um bug do compilador. Verifique a versão mais recente do compilador. Se o bug ainda estiver presente, envie um relatório de bug.

David Heffernan
fonte