7

私は VS2010 ベータ 2 で 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);
}

したがって、Reflector の F# コードの 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 

(参照アセンブリは簡潔にするために削除されています)。

4

1 に答える 1

18

maxstackの違いは、C#コンパイラが最初のメソッドを«light»メソッド本体ヘッダーでコンパイルするという事実によるものです。このヘッダーは、コードが小さい場合は常に使用され、例外やローカルはありません。その場合、maxstackは指定されず、デフォルトは8です。

F#コンパイラは、«fat»メソッドの本文ヘッダーを使用しており、計算した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は取得されません。

于 2009-10-23T10:29:44.253 に答える