F#コードのリリースビルドのNOP
-
06-07-2019 - |
質問
VS2010 beta2でF#を使用していますが、F#を初めて使用するため、一般的な例の1つを選択し、次のような階乗関数を実装しました。
let rec factorial n =
if n <= 1 then 1 else n * factorial (n - 1);;
これをビルドしてReflectorで生成されたコードを見ると、対応するC#コードが得られます:
public static int Factorial(int n) {
if (n <= 1)
return 1;
return n * Factorial(n - 1);
}
したがって、F#コードのReflectorのC#表現をコンパイルすると、同一のILが得られると予想されます。
ただし、これらのスニペットの両方をリリースモードでコンパイルし、生成されたILを比較すると、それらは異なります(機能的には同じですが、少し異なります)。
C#実装は次のようにコンパイルされます。
.method public hidebysig static int32 Factorial(int32 n) cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: ldc.i4.1
L_0002: bgt.s L_0006
L_0004: ldc.i4.1
L_0005: ret
L_0006: ldarg.0
L_0007: ldarg.0
L_0008: ldc.i4.1
L_0009: sub
L_000a: call int32 TestApp.Program::Factorial(int32)
L_000f: mul
L_0010: ret
}
F#実装は次のようにコンパイルされます。
.method public static int32 factorial(int32 n) cil managed
{
.maxstack 5 <=== Different maxstack
L_0000: nop <=== nop instruction?
L_0001: ldarg.0
L_0002: ldc.i4.1
L_0003: bgt.s L_0007
L_0005: ldc.i4.1
L_0006: ret
L_0007: ldarg.0
L_0008: ldarg.0
L_0009: ldc.i4.1
L_000a: sub
L_000b: call int32 FSharpModule::factorial(int32)
L_0010: mul
L_0011: ret
}
生成されたコードは、maxstackとF#メソッドの追加のNOP命令を除いて同一です。
これはおそらく重要ではありませんが、F#コンパイラがリリースビルドにNOPを挿入する理由について興味があります。
誰もがその理由を説明できますか?
(F#コンパイラがC#コンパイラと同じレベルの実世界テストを行っていないことは完全に承知していますが、これは非常に明白であるため、捕まえられると思います)。
編集:コンパイルコマンドは次のとおりです
C:\Program Files\Microsoft F#\v4.0\fsc.exe -o:obj\Release\FSharpLib.dll
--debug:pdbonly --noframework --define:TRACE --optimize+
--target:library --warn:3 --warnaserror:76 --vserrors --utf8output --fullpaths
--flaterrors "C:\Temp\.NETFramework,Version=v4.0.AssemblyAttributes.fs" Module1.fs
(簡潔にするために参照アセンブリを削除)。
解決
maxstackの違いは、C#コンパイラが最初のメソッドを&#171; light&#187;でコンパイルするためです。メソッド本体ヘッダーは、コードが小さい場合に常に使用され、例外もローカルもありません。その場合、maxstackは指定されず、デフォルトは8です。
F#コンパイラは&#171; fat&#187;を使用していますメソッド本体ヘッダー、および計算したmaxstackを指定します。
nopについては、デバッグモードでコンパイルしているためです。メソッド本体は常にnopで始まります。 fsharp / ilxgen.mlから参照してください:
// Add a nop to make way for the first sequence point. There is always such a
// sequence point even when zapFirstSeqPointToStart=false
do if mgbuf.cenv.generateDebugSymbols then codebuf.Add(i_nop);
デバッグシンボルなしで階乗をコンパイルした場合、nopは取得されません。