В режиме выпуска поведение кода не такое, как ожидалось

131

Следующий код генерирует разные результаты в режиме отладки и в режиме выпуска (с использованием 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;
}

Результат режима отладки, как и ожидалось:

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

Вывод режима выпуска, где результат i: 15 неверен:

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

Выбрав «Оптимизация -> Не оптимизировать» в Visual Studio в режиме выпуска, выходной результат будет правильным. Однако я хотел бы знать, почему процесс оптимизации может приводить к ошибочному выводу.


Обновить:

По предложению Mohit JainBy, печатает:

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

Выход режима выпуска правильный:

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
Это похоже на ошибку компилятора (и притом довольно значительную).
WhozCraig 09
1
@WhozCraig Просто обновляет вывод i * 16в сообщении, и результат правильный.
Лоррис Лин
4
@juanchopanza: Исходя из моего опыта работы с MS и исправлениями для VS, они исправляют такие ошибки после того, как узнали о них, но не применяют эти исправления к более старым версиям VS, поэтому, если кто-то по какой-то причине вынужден использовать старую версию VS, то такие ошибки остаются до тех пор, пока не удастся перейти на более новую версию.
Kaiserludi 09
2
FWIW, это отлично работает с предстоящей Visual Studio 2015
ismail

Ответы:

115

Это интересно, по крайней мере, с исторической точки зрения. Я могу воспроизвести проблему с VC 2008 (15.00.30729.01) и VC 2010 (16.00.40219.01) (таргетинг на 32-битный x86 или 64-битный x64). Проблема не возникает ни с одним из компиляторов, которые я пробовал, начиная с VC 2012 (17.00.61030).

Команда, которую я использовал для компиляции: cl /Ox vc15-bug.cpp /FAsc

Поскольку VC 2008 (и 2010) довольно старый и исправление существует уже несколько лет, я не думаю, что вы можете ожидать каких-либо действий от Microsoft, кроме использования более нового компилятора (хотя, возможно, кто-то может предложить обходной путь).

Проблема заключается в том, что проверка для определения необходимости принудительного 255выполнения значения выполняется на основе количества циклов, а не фактического результата i * 16выражения. И компилятор просто неправильно подсчитывает, когда он должен начать принудительное присвоение значения 255. Понятия не имею, почему это происходит - это просто эффект, который я вижу:

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

Обновление : все версии VC, которые я установил ранее, чем VC 2008, имеют одну и ту же ошибку, кроме VC6 - компиляция программы приводит к сбою компилятора VC6:

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

Так что это ошибка, которая в той или иной форме сохранялась в MSVC более 10 лет!

Майкл Берр
источник
Если моя память о времени сборки x86 верна, причина для сравнения с esi, а не с eax - comp eax, 255 вызовет остановку конвейера, поскольку eax только что был написан.
Loren Pechtel
3
Мое предположение (преобразования): результат> 255, результат / 16> 255/16, i> 15, i <= 14
teki
Очень интересно! Также, если вы измените сравнение с result > 255на, result >= 255оно будет вести себя правильно. В VS2010 , что изменения cmp esi, 14в cmp esi, 16jleк jl).
opello
16

Если предположить, что сообщаемые вами факты верны, это будет ошибкой компилятора. Проверьте последнюю версию компилятора. Если ошибка все еще присутствует, отправьте отчет об ошибке.

Дэвид Хеффернан
источник