誰かが MSIL のこれらの数行を説明できますか?
-
22-08-2019 - |
質問
誰かが MSIL のこれらの数行を説明できますか?値を評価スタックからローカル変数に移動し、すぐに元に戻して返すのはなぜですか?
次の MSIL コードは、単一の引数 (文字列) を読み込み、bool を返すメソッドを呼び出し、その bool 値を返します。私が理解できないのは、なぜ stloc.0 を呼び出してメソッドの戻り値をローカル変数に格納し、次にラベル付けされた次の行への明示的な無条件制御転送を実行し (不要に見える)、値をすぐに戻すためだけに実行するのかということです。評価スタックを返す前に。
.maxstack 1
.locals init ([0] bool CS$1$0000)
L_0000: nop
L_0001: ldarg.0
L_0002: call bool FuncNameNotImporant::MethodNameNotImporant(string)
L_0007: stloc.0
L_0008: br.s L_000a
L_000a: ldloc.0
L_000b: ret
なぜこれが行われるのかについての私の推測では、評価スタックの値が実際にブール値であることを返す前に、ある種の型チェックを実行するためです。しかし、私は次の行への明示的なジャンプについては無知です。つまり、とにかくそこに行きませんか?このメソッドの C# ソース コードは 1 行であり、メソッドの結果を返します。
解決
デバッグ モードでコンパイルされたコードを使用して、この関数をデバッガーで開くと、次のようになります。
bool foo(string arg)
{
return bar(arg);
}
設定できるブレークポイントは 3 つあります。
- 関数の左中括弧。
- 「戻り」線で。
- 関数の閉じ中括弧。
左中括弧にブレークポイントを設定することは、「この関数が呼び出されたときにブレークする」ことを意味します。そのため、メソッドの先頭に no-op 命令があるのです。ブレークポイントが左中括弧に設定されている場合、デバッガーは実際にブレークポイントを no-op に設定します。
右中括弧にブレークポイントを設定することは、「この関数が終了するときにブレークする」ことを意味します。これを実現するには、関数の IL にブレーク ポイントを設定できる単一の return 命令が必要です。コンパイラは、一時変数を使用して戻り値を格納し、変換することでこれを可能にします。
return retVal;
の中へ
$retTmp = retVal;
goto exit;
次に、メソッドの最後に次のコードを挿入します。
exit:
return $ret;
また、デバッグ モードでは、コンパイラは生成するコードについて愚かです。基本的に次のようなことを行います。
GenerateProlog();
foreach (var statement in statements)
{
Generate(statement);
}
GenerateEpilog();
あなたの場合、次のことが表示されます。
return foo(arg);
次のように翻訳されます:
; //this is a no-op
bool retTemp = false;
retTemp = foo(arg);
goto exit;
exit:
return retTemp;
コンパイラが「スライディング ウィンドウの最適化」を実行していた場合、そのコードを調べて冗長性があることに気付く可能性があります。ただし、コンパイラは通常、デバッグ モードではそれを行いません。コンパイラの最適化では、変数の削除や命令の並べ替えなどが行われるため、デバッグが困難になります。デバッグ ビルドの目的はデバッグを有効にすることであるため、最適化をオンにすることは適切ではありません。
リリース ビルドでは、コードはそのようにはなりません。これは、コンパイラーが左中括弧と右中括弧でブレークポイントを有効にする特別なコードを導入せず、次のコードがコンパイルされるだけになるためです。
return bar(arg);
それは非常にシンプルに見えます。
ただし、注意すべき点が 1 つあります。それは、製品版ビルドであっても、C# コンパイラーがスライディング ウィンドウの最適化をあまり行うとは思えないということです。これは、これらの最適化のほとんどが基礎となるプロセッサ アーキテクチャに依存しており、JIT コンパイラによって実行されるためです。C# コンパイラーで最適化を実行すると、それがプロセッサーに依存しない最適化であっても、コードを最適化する JIT の機能が妨げられる可能性があります (最適化されていないコード生成によって生成されたパターンを検索し、高度に最適化された IL が見つかった場合は、JIT がコードを最適化する機能を妨げる可能性があります)。混乱した)。したがって、通常、管理されたコード コンパイラーはそれらを実行しません。デッドコードの検出やライブ変数の分析など、(JIT が実行時に実行したくない) いくつかの「コストのかかること」を実行しますが、スライディング ウィンドウの最適化によって解決される問題には対処しません。
他のヒント
あなたは、デバッグまたはリリースモードでコンパイルされていますか?リリースモードでは私が取得ます:
.method private hidebysig static bool Test1(string arg) cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: call bool FuncNameNotImportant::MethodNameNotImportant(string)
L_0006: ret
}
デバッガのサポートのために、おそらくあなたが見ているされて分岐します。