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 weblogCLRのリフレクションや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もたまたまだった。う〜ん。