IL Visualizer
青柳さんのところで DynamicMethod 用の DebuggerVisualizer の情報を発見。
新バージョンのIL Visualizerがあったので、こっちを入れてみました。
http://blogs.msdn.com/haibo_luo/archive/2006/11/16/take-two-il-visualizer.aspx
インストールは記事の下のところにある ILVisualizer.zip を落としてきてコンパイルしたら完了。My Documents\Visual Studio 2005\Visualizers に必要なdllを勝手に入れてくれます。ILStream(IL Visualizer)とIL Monitorの2タイプの表示手段が追加されます。
どちらのVisualizerも表示されるILは同じなので、お好みの方を使えばOKです。IL MonitorはローカルでTCP:22017を使うそうな。
IL Monitorはマイドキュメント下に自動でインストールされないので注意。また、IL Monitorを使う場合はあらかじめILMonitor.exeを起動しておく必要があります。
使い方は、DynamicMethodで作ったインスタンスをウォッチウィンドウか、直接コード中の変数にマウスをあわせるかして、虫眼鏡アイコンで Send to IL Monitor か IL Visualizer をクリックすればILが表示されます!スゴイ。cool!!(ILStreamの挙動がいまいちだけど、本気製品じゃないツールなんてこんなもんだよね。)
Haibo Luo's weblogはCLRのリフレクションやDynamicMethodまわりを担当してた方が書いてるんですね。今はIronPython担当らしい。なるほど適任ですね。
で、昨日のコードで生成されたILはこんな。S1構造体に対するIL。
IL_0000: ldarga.s V_0 IL_0002: nop IL_0003: nop IL_0004: nop IL_0005: constrained. S1 IL_000b: callvirt Char get_Value()/ConsoleApplication2.S1 IL_0010: stloc.0 IL_0011: ldloca.s V_0 IL_0013: nop IL_0014: nop IL_0015: nop IL_0016: ldarga.s V_1 IL_0018: nop IL_0019: nop IL_001a: nop IL_001b: constrained. S1 IL_0021: callvirt Char get_Value()/ConsoleApplication2.S1 IL_0026: constrained. Char IL_002c: callvirt Int32 CompareTo(Char)/System.IComparable`1[System.Char] IL_0031: ret
あれ、こんなにNOPが出てるのか…(T-T)
ILもアライメントの制限があるの?って奇数になってるか。それとも別の理由?まあいいや。
S2構造体に対する方は、上のS1がS2に変わっただけ。うーん、正しいような…。
インターフェイスのありなしで変わることと言えばJITのインライン展開の有無が浮かぶけど、関係あるのかなぁ。Debug版で失敗してるんですけどね…。
前に書いたとおり、DateTime構造体のDayプロパティを比較するのは問題なく動きます。
ちなみにEmitWriteLine入れたらNUnitがだまって落ちるようになったり(^^;
茨の道を突き進むのもそろそろ諦め時か…?
って書いたらNOPの理由がわかった!このコードが悪いんだ。
ilgen.Emit( OpCodes.Ldloca_S, 0 );
↑こうじゃなくて、↓こうが正解。
ilgen.Emit( OpCodes.Ldloca_S, ( byte ) 0 );
直したらNOPは消えた。バグはそのまま。
さらにこれを調べるためにMSDNライブラリ見たら、そのものズバリっぽい記述を発見。stloc.0 のところに問題がありそう。
4 バイト長より小さい整数値を保持しているローカルに格納すると、スタックからローカル変数に移動するときに値が切り詰められます。浮動小数点値は、ネイティブ サイズ (F 型) から引数に関連付けられたサイズに丸められます。
だそうで。S2構造体のcharをlongに変えたら正しく動いた!いやいや、切り詰めたところで問題ないなぁ…。longもたまたまだった。う〜ん。