NOP在F#代码的发布版本中

Bri*_*sen 6 f# cil visual-studio-2010-beta-2

我在VS2010 beta2中玩F#,因为我是F#的新手,所以我选择了一个常见的例子并继续实现了一个阶乘函数:

let rec factorial n =
  if n <= 1 then 1 else n * factorial (n - 1);;
Run Code Online (Sandbox Code Playgroud)

如果我构建它并查看Reflector中生成的代码,我会得到相应的C#代码:

public static int Factorial(int n) {
   if (n <= 1) 
      return 1;

      return n * Factorial(n - 1);
}
Run Code Online (Sandbox Code Playgroud)

因此,如果我编译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 
}
Run Code Online (Sandbox Code Playgroud)

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 
}
Run Code Online (Sandbox Code Playgroud)

除了不同的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 
Run Code Online (Sandbox Code Playgroud)

(为简洁起见,删除了引用的程序集).

Jb *_*ain 17

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);
Run Code Online (Sandbox Code Playgroud)

如果我在没有调试符号的情况下编译你的阶乘,我就不会得到一个nop.

  • 您发布的命令行包含`--debug:pdbonly`,它会触发包含nop. (4认同)