パスワードを忘れた? アカウント作成
229262 journal

stehanの日記: 0 < 0 は true らしいぞ 14

日記 by stehan
http://srad.jp/~stehan/journal/509411 の続き。
if( 0 < i ) つけて回避できたのはサンプルだけで、実コードでは i に 0 が入っていても実行されてしまったよ、あははははー
というわけで回避策は
while( -1 < --i )

こんなコンパイラでたまにしかクラッシュしない Windows みたいなOS作れるマイクロソフトの技術力はたいしたものだなあ
この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • 再現します (スコア:2, 参考になる)

    by moca (33770) on 2010年06月15日 16時24分 (#1780416) 日記

    構成プロパティ->全般->[プログラム全体の最適化]を「リンク時のコード生成を使用」から「プログラム全体の最適化なし」にすると直りました。
    cl /O1 /GL test.cpp のように /O1 /O2 /Oxなどと GL(/LTCG) を組み合わせると発生して、 O1, O2単独やGL単独では発生しません。
    VC 2008 SP1/CL 15.00.30729.01 for 80x86/LINK: 9.00.30729.01 x86版
    includeヘッダは以下のとおり。
    #include <windows.h>
    #include <stdio.h>
    #include <tchar.h>

  • なんか条件の数値を変えるというのが美しくない気がしたので。

    • 多分それでも良いんでしょうけれど、コンパイラの不具合回避してる時点で美意識もクソも無いよってのが本音。

      でも本当に怖いのは、回避後も条件を変えたらまた発症しないかとか、
      過去に書いた類似コードにも見逃された不具合が隠れてないかとか、
      もうどうしたものやら。

      どうせパフォーマンスを要求されるようなコードなんか書かないし、
      いっそデフォルト最適化なしで生きていこうかしら。
      親コメント
  • by nekurai (6253) on 2010年06月15日 16時02分 (#1780398) 日記
    VS2008 Pro ですが、debug/release 共正常動作。
    他のオプションどっかいじると起きるのかなぁ?

    # 意味ないと思って sleep を code から抜いたけど
    # まさかそれで差が…出るとは思いたくないし (笑)
    • by stehan (37041) on 2010年06月15日 16時20分 (#1780410) 日記

      うちも VS2008 Professional でパッチも全部当ててます。
      OS は xp pro 32bit
      W32 コンソールアプリのプロジェクトで、Sleep() 呼ぶために windows.h を追加で include した他は貼ったとおり。

      ちなみにプロジェクトのプロパティで最適化オフでは正常動作します。一番手軽な回避策はこれですかね。

      親コメント
      • by nekurai (6253) on 2010年06月15日 16時50分 (#1780429) 日記

        #1780417 [srad.jp]さんが書いてるのを見て sleep 追加したら再現しました。

        ううむ、最適化恐るべし (笑)
        20 年ぐらい前なら結果がおかしかったらすぐにアセンブラ出力でチェック
        だったけど最近はコンパイラをそこそこ信用しちゃってるからなぁ。
        やっぱり信用するのは怖い。

        ちなみに VS2010 Premium では debug/release 共とりあえず問題なし。
        もっとも sleep の前後に余計なコードとか入れても大丈夫かどうかまで
        ちゃんと検証は出来てません。

        親コメント
  • VC++ 2008を持っていないので手伝えないが、一応念のために、調べておいた方が良いと思うんだ。

    int
    main( int argc, char *argv[] )
    {
        int a, b;
    #define VAL   1
        if ( VAL < VAL )    printf( "%d: CONST < CONST fail\n", VAL );
        a = VAL;
        if ( a < VAL )      printf( "%d: VAL   < CONST fail\n", VAL );
        b = VAL;
        if ( VAL < b )      printf( "%d: CONST < VAL   fail\n", VAL );
        if ( a < b )        printf( "%d: VAL   < VAL   fail\n", VAL );
        if ( a < a )        printf( "%d: VALA  < VALA  fail\n", VAL );
     
    #define VAL   0
        if ( VAL < VAL )    printf( "%d: CONST < CONST fail\n", VAL );
        a = VAL;
        if ( a < VAL )      printf( "%d: VAL   < CONST fail\n", VAL );
        b = VAL;
        if ( VAL < b )      printf( "%d: CONST < VAL   fail\n", VAL );
        if ( a < b )        printf( "%d: VAL   < VAL   fail\n", VAL );
        if ( a < a )        printf( "%d: VALA  < VALA  fail\n", VAL );
     
    #define VAL   -1
        if ( VAL < VAL )    printf( "%d: CONST < CONST fail\n", VAL );
        a = VAL;
        if ( a < VAL )      printf( "%d: VAL   < CONST fail\n", VAL );
        b = VAL;
        if ( VAL < b )      printf( "%d: CONST < VAL   fail\n", VAL );
        if ( a < b )        printf( "%d: VAL   < VAL   fail\n", VAL );
        if ( a < a )        printf( "%d: VALA  < VALA  fail\n", VAL );
    }

    …それにしてもなんという落とし穴…

    --
    fjの教祖様
    • if では特に問題はありませんでした。
      http://srad.jp/comments.pl?sid=498582&cid=1780417 [srad.jp] で調べて頂いたとおり、問題は while() の噛み砕きすぎなんでしょう。
      こうなると一時的であってもなぜ if で括って回避できたかが謎ですね、
      いまどきのコンパイラであれば、直後の類似条件の while() を見て無視される方が順当でしょうから。

      親コメント
      • 大体挙動が見えてきました。
        ↓のパターンで今回の件が大体説明出来てるんじゃないかと。
        最適化でのバグの問題、最適化で消えてしまうコードの問題、
        関数呼び出しかインライン展開かの問題の 3 つがごちゃまぜっぽい。

        a) オリジナルコードをベースとしたテスト

        /Ox と /GL を共につけない, if で対策しない
        → 関数呼び出しコード、関数の中は正しいコード
        → 正しい結果

        /Ox と /GL を共につける, if で対策しない
        → 関数呼び出しコード、関数の中は正しくないコード
        → 正しくない結果

        /Ox と /GL を共につけない, if で対策する
        → 関数呼び出しコード、if は最適化されないので消えない、 関数の中は正しいコード
        → 正しい結果

        /Ox と /GL を共につける, if で対策する
        → インライン展開コード、if は最適化されて消える、関数の中は正しくないコード
        → 正しい結果 (関数はコケてるけど実際にはインラインコードを実行している)

        b) count_down 内の sleep 呼び出しをコメントアウトしたテスト

        /Ox と /GL を共につけない, if で対策しない
        → 関数呼び出しコード、関数の中は正しいコード
        → 正しい結果

        /Ox と /GL を共につける, if で対策しない
        → インライン展開コード、関数の中は正しくないコード
        → 正しい結果 (sleep が無くなってインライン展開しやすくなった???)

        /Ox と /GL を共につけない, if で対策する
        → 関数呼び出しコード、if は最適化されないので消えない、 関数の中は正しいコード
        → 正しい結果

        /Ox と /GL を共につける, if で対策する
        → インライン展開コード、if は最適化されて消える、関数の中は正しくないコード
        → 正しい結果 ( a と同じパターン)

        c) _tmain 内に int a=2; と書いて関数呼び出しを count_down(a--); と書き換え
         count_down 内の sleep 呼び出しは元に戻したテスト

        /Ox と /GL を共につけない, if で対策しない
        → 関数呼び出しコード、関数の中は正しいコード
        → 正しい結果

        /Ox と /GL を共につける, if で対策しない
        → 関数呼び出しコード、関数の中は正しくないコード
        → 正しくない結果

        /Ox と /GL を共につけない, if で対策する
        → 関数呼び出しコード、if は最適化されないので消えない、 関数の中は正しいコード
        → 正しい結果

        /Ox と /GL を共につける, if で対策する
        → 関数呼び出しコード、if は最適化されて消える、関数の中は正しくないコード
        → 正しくない結果 (何等かの形で関数呼び出しになってしまうとアウト)
        親コメント
  • by Anonymous Coward on 2010年06月15日 16時25分 (#1780417)
    生成されたコードを見ると、do{i--;printf~;}while(i>0)みたいな感じになってますね。
    条件式を i-->0に変えたり Sleepを削るようなわずかな変更で関数ごとインライン展開されて期待通りに動く辺り、最適化でどつぼにはまってる感じです。
    リンク時のコード生成を無効にすると回避できましたが、怖いですね。
  • by Anonymous Coward on 2010年06月16日 17時35分 (#1781033)
    それでMicrosoft Connect等に報告はしたんでしょうか MSに伝わらなきゃVS2008 SP2が出ても直りゃしません
typodupeerror

吾輩はリファレンスである。名前はまだ無い -- perlの中の人

読み込み中...