stehanの日記: 0 < 0 は true らしいぞ 14
日記 by
stehan
http://srad.jp/~stehan/journal/509411 の続き。
if( 0 < i ) つけて回避できたのはサンプルだけで、実コードでは i に 0 が入っていても実行されてしまったよ、あははははー
というわけで回避策は
while( -1 < --i )
こんなコンパイラでたまにしかクラッシュしない Windows みたいなOS作れるマイクロソフトの技術力はたいしたものだなあ
if( 0 < i ) つけて回避できたのはサンプルだけで、実コードでは i に 0 が入っていても実行されてしまったよ、あははははー
というわけで回避策は
while( -1 < --i )
こんなコンパイラでたまにしかクラッシュしない Windows みたいなOS作れるマイクロソフトの技術力はたいしたものだなあ
再現します (スコア:2, 参考になる)
構成プロパティ->全般->[プログラム全体の最適化]を「リンク時のコード生成を使用」から「プログラム全体の最適化なし」にすると直りました。
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>
「0 <= --i」じゃダメか? (スコア:1)
なんか条件の数値を変えるというのが美しくない気がしたので。
Re:「0 <= --i」じゃダメか? (スコア:1)
でも本当に怖いのは、回避後も条件を変えたらまた発症しないかとか、
過去に書いた類似コードにも見逃された不具合が隠れてないかとか、
もうどうしたものやら。
どうせパフォーマンスを要求されるようなコードなんか書かないし、
いっそデフォルト最適化なしで生きていこうかしら。
Re:「0 = --i」じゃダメか? (スコア:1)
_tmain() の中で一度でも count_down( -1 ) で呼ぶとなぜか治まったので、
意外とこれ、限られた条件以外では正しくコンパイルされるのかもしれません。
だからって過去のコードを放っとくわけにもいかないよなぁ、やっぱ。
再現しないなぁ… (スコア:1)
他のオプションどっかいじると起きるのかなぁ?
# 意味ないと思って sleep を code から抜いたけど
# まさかそれで差が…出るとは思いたくないし (笑)
Re:再現しないなぁ… (スコア:1)
うちも VS2008 Professional でパッチも全部当ててます。
OS は xp pro 32bit
W32 コンソールアプリのプロジェクトで、Sleep() 呼ぶために windows.h を追加で include した他は貼ったとおり。
ちなみにプロジェクトのプロパティで最適化オフでは正常動作します。一番手軽な回避策はこれですかね。
Re:再現しないなぁ… (スコア:1)
#1780417 [srad.jp]さんが書いてるのを見て sleep 追加したら再現しました。
ううむ、最適化恐るべし (笑)
20 年ぐらい前なら結果がおかしかったらすぐにアセンブラ出力でチェック
だったけど最近はコンパイラをそこそこ信用しちゃってるからなぁ。
やっぱり信用するのは怖い。
ちなみに VS2010 Premium では debug/release 共とりあえず問題なし。
もっとも sleep の前後に余計なコードとか入れても大丈夫かどうかまで
ちゃんと検証は出来てません。
それってつまり 0 < 0 だけど -1 < -1 じゃないってこと? (スコア:1)
VC++ 2008を持っていないので手伝えないが、一応念のために、調べておいた方が良いと思うんだ。
…それにしてもなんという落とし穴…
fjの教祖様
Re:それってつまり 0 0 だけど -1 -1 じゃないってこと? (スコア:1)
if では特に問題はありませんでした。
http://srad.jp/comments.pl?sid=498582&cid=1780417 [srad.jp] で調べて頂いたとおり、問題は while() の噛み砕きすぎなんでしょう。
こうなると一時的であってもなぜ if で括って回避できたかが謎ですね、
いまどきのコンパイラであれば、直後の類似条件の while() を見て無視される方が順当でしょうから。
Re:それってつまり 0 0 だけど -1 -1 じゃないってこと? (スコア:2, 参考になる)
↓のパターンで今回の件が大体説明出来てるんじゃないかと。
最適化でのバグの問題、最適化で消えてしまうコードの問題、
関数呼び出しかインライン展開かの問題の 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 は最適化されて消える、関数の中は正しくないコード
→ 正しくない結果 (何等かの形で関数呼び出しになってしまうとアウト)
Re:それってつまり 0 0 だけど -1 -1 じゃないってこと? (スコア:1)
詳細な調査をありがとうございます。他の方々も。
でも毎度毎度こんな検証しなきゃいけないわけ?もしかして。
他にもこんな不具合が隠れてなきゃ良いんですけど。
VS2005でも再現しますね (スコア:0)
条件式を i-->0に変えたり Sleepを削るようなわずかな変更で関数ごとインライン展開されて期待通りに動く辺り、最適化でどつぼにはまってる感じです。
リンク時のコード生成を無効にすると回避できましたが、怖いですね。
世界の隅っこでバグを叫ぶ (スコア:0)
Re:世界の隅っこでバグを叫ぶ (スコア:1)