リリースモードでは、コードの動作は期待どおりではありません


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では、新しいバージョンにアップグレードできるようになるまで、そのようなバグに悩まされています。
カイザールディ2015

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 2008より前にインストールしたVCのすべてのバージョンに同じバグがありますが、VC6は例外です-プログラムをコンパイルするとVC6コンパイラがクラッシュします。

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

これは、MSVCで何らかの形で10年以上続いたバグです。


私のx86アセンブリタイミングのメモリが正しい場合、eaxがcomp eaxではなくesiと比較される理由は、eaxが書き込まれたばかりなので、255がパイプラインのストールを引き起こします。
Loren Pechtel、2015

3
私の推測(変換):結果> 255、結果/ 16> 255/16、i> 15、i <= 14
teki

とても興味深い!また、比較をからresult > 255に変更すると、result >= 255正しく動作します。VS2010では、それが(およびtoに)変更さcmp esi, 14れます。cmp esi, 16jlejl
opello 2015

16

報告された事実が正しいと仮定すると、これはコンパイラのバグになります。コンパイラの最新バージョンを確認してください。バグがまだ存在する場合は、バグレポートを送信してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.