前回は次のコードを実行させると失敗するところで終わりました。というわけで、今回はこの不具合を解決することに全力を注ごうと思います。
.assembly extern mscorlib {} .assembly outnum {} .method static public void main () cil managed { .entrypoint ldc.i4.1 ldc.i4.2 ldc.i4.3 ldc.i4.4 ldc.i4.5 ldc.i4.6 ldc.i4.7 ldc.i4.8 ldc.i4.s 9 ldc.i4.s 10 add add add add add add add add add call void [mscorlib]System.Console::WriteLine(int32) ret }
ソースコードには間違いはないようです。こんな時は逆アセンブラの登場です。
.assembly extern mscorlib { .ver 0:0:0:0 } .assembly outnum { .ver 0:0:0:0 } .module outnum.EXE // MVID: {71F6BFAC-F405-4546-BBF0-310B33FF02ED} .imagebase 0x00400000 .subsystem 0x00000003 .file alignment 512 .corflags 0x00000001 // Image base: 0x00d90000 // =============== GLOBAL FIELDS AND METHODS =================== //Global methods //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .method public static void main() cil managed { .entrypoint // コード サイズ 27 (0x1b) .maxstack 8 IL_0000: ldc.i4.1 IL_0001: ldc.i4.2 IL_0002: ldc.i4.3 IL_0003: ldc.i4.4 IL_0004: ldc.i4.5 IL_0005: ldc.i4.6 IL_0006: ldc.i4.7 IL_0007: ldc.i4.8 IL_0008: ldc.i4.s 9 IL_000a: ldc.i4.s 10 IL_000c: add IL_000d: add IL_000e: add IL_000f: add IL_0010: add IL_0011: add IL_0012: add IL_0013: add IL_0014: add IL_0015: call void [mscorlib]System.Console::WriteLine(int32) IL_001a: ret } // end of global method main
色々追加されていますが、今回注目すべき点は.maxstackです。
ちょっと前にスタックに積んで計算のモデルを書きましたよね?その時は入れたぶんだけスタックが延びていきましたが、現実には色々な制限が付きます。要するにスタックには最大サイズがあるのです。そして、この値は何も設定しないと「8」になります。
これで問題が解決しました。とりあえずは「.maxstack」の値を「10」にすればいいようです。
.assembly extern mscorlib {} .assembly outnum {} .method static public void main () cil managed { .entrypoint .maxstack 10 ldc.i4.1 ldc.i4.2 ldc.i4.3 ldc.i4.4 ldc.i4.5 ldc.i4.6 ldc.i4.7 ldc.i4.8 ldc.i4.s 9 ldc.i4.s 10 add add add add add add add add add call void [mscorlib]System.Console::WriteLine(int32) ret }
これで問題は解決しました。このようにスタックマシンのスタックサイズは実行時に大きな意味を持つようです。
スタック使用数が少ない場合も使うギリギリの値にスタックの使用数を調整した方がパフォーマンスが上がると考えられます。ですので、今までのソースコードにも「.maxstack」を使用してみましょう。
.assembly extern mscorlib {} /*入れても入れなくても良い。*/ .assembly hello {} .method static public void main() cil managed { .entrypoint .maxstack 1 ldstr "Hello world!" //スタックに文字列を積む call void [mscorlib]System.Console:: WriteLine(class System.String) //関数をコール ret //終了 }
こんな感じでスタックは節約した方が良さそうです。
ここまで読んでくれた方の中でILに詳しい方。スタック領域を少々大きく取っても連続の方がパフォーマンスが良かったりしますか?教えて頂けるとありがたいです。