Dalam mode rilis, perilaku kode tidak seperti yang diharapkan

131

Kode berikut menghasilkan hasil yang berbeda di bawah mode debug dan mode rilis (menggunakan 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;
}

Output dari mode debug, yang seperti yang diharapkan:

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

Output mode rilis, di mana hasil i: 15 tidak benar:

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

Dengan memilih "Optimasi -> Tidak untuk mengoptimalkan" di Visual Studio dalam mode rilis, hasil output akan benar. Namun saya ingin tahu mengapa proses optimasi dapat menyebabkan output yang salah.


Memperbarui:

Seperti yang disarankan oleh Mohit JainBy, dicetak oleh:

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

Output mode rilis sudah benar:

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
sumber
15
Itu terlihat seperti bug kompiler (dan yang cukup signifikan pada saat itu).
WhozCraig
1
@WhozCraig Baru saja memperbarui output i * 16dalam pos, dan hasilnya benar.
Lorris Lin
4
@ juanchopanza: Dari pengalaman saya dengan MS dan perbaikan bug ke VS mereka memperbaiki bug seperti itu setelah mereka mendapat informasi tentang mereka, tetapi tidak menerapkan perbaikan tersebut ke versi VS yang lebih lama, jadi jika seseorang karena suatu alasan terpaksa menggunakan versi yang lebih lama dari VS, maka seseorang terjebak dengan bug seperti itu sampai seseorang dapat meng-upgrade ke versi yang lebih baru.
Kaiserludi
2
FWIW ini berfungsi dengan baik dengan Visual Studio 2015 mendatang
ismail

Jawaban:

115

Ini menarik, setidaknya dari perspektif sejarah. Saya dapat mereproduksi masalah dengan VC 2008 (15.00.30729.01) dan VC 2010 (16.00.40219.01) (menargetkan 32-bit x86 atau 64-bit x64). Masalahnya tidak terjadi dengan salah satu kompiler yang saya coba mulai dengan VC 2012 (17.00.61030).

Perintah yang saya gunakan untuk mengkompilasi: cl /Ox vc15-bug.cpp /FAsc

Karena VC 2008 (dan 2010) agak lama dan perbaikannya telah berlangsung selama beberapa tahun sekarang, saya tidak berpikir Anda dapat mengharapkan tindakan apa pun dari Microsoft kecuali untuk menggunakan kompiler yang lebih baru (meskipun mungkin seseorang dapat menyarankan solusi).

Masalahnya adalah tes untuk menentukan apakah nilai harus dipaksa 255dilakukan berdasarkan jumlah loop daripada hasil aktual dari i * 16ekspresi. Dan kompiler hanya mendapatkan hitungan yang salah ketika harus mulai memaksa nilai 255. Saya tidak tahu mengapa itu terjadi - itu hanya efek yang saya lihat:

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

Pembaruan : Semua versi VC yang saya instal lebih awal dari VC 2008 memiliki bug yang sama, kecuali VC6 - mengkompilasi program membuat crash kompilator VC6:

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

Jadi ini adalah bug yang bertahan di MSVC dalam satu atau lain bentuk selama lebih dari 10 tahun!

Michael Burr
sumber
Jika ingatan saya tentang waktu perakitan x86 benar alasan perbandingan dengan esi daripada eax adalah comp eax, 255 akan menyebabkan pipeline stall karena eax baru saja ditulis.
Loren Pechtel
3
Tebakan saya (transformasi): hasil> 255, hasil / 16> 255/16, i> 15, i <= 14
teki
Sangat menarik! Juga jika Anda mengubah perbandingan dari result > 255ke result >= 255berperilaku dengan benar. Dalam VS2010 yang berubah cmp esi, 14menjadi cmp esi, 16(dan jleke jl).
Opello
16

Anggap fakta yang Anda laporkan benar, ini akan menjadi bug penyusun. Periksa versi kompiler terbaru. Jika bug masih ada, kirim laporan bug.

David Heffernan
sumber