У режимі випуску поведінка коду не така, як очікувалося


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

1
@WhozCraig Просто оновлює результат i * 16публікації, і результат правильний.
Lorris Lin

4
@juanchopanza: З мого досвіду роботи з MS та виправленнями в VS вони виправляють такі помилки після того, як про них поінформували, але не застосовують ці виправлення до старих версій VS, тож якщо хтось з якихось причин змушений використовувати старішу версію VS, тоді застрягли такі помилки, поки не вдасться оновити до нової версії.
Kaiserludi

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.
Лорен Печтел

3
Моя здогадка (перетворення): результат> 255, результат / 16>
255/16

Дуже цікаво! Крім того, якщо ви зміните порівняння з result > 255до result >= 255він поводиться правильно. У VS2010 це змінюється cmp esi, 14на cmp esi, 16jleна jl).
opello

16

Якщо припустимо, що ваші повідомлені факти є правильними, це буде помилка компілятора. Перевірте останню версію компілятора. Якщо помилка все ще присутня, надішліть звіт про помилку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.