【问题标题】:NOP in release build of F# codeF# 代码的发布版本中的 NOP
【发布时间】:2009-10-23 10:20:01
【问题描述】:

我在 VS2010 beta2 中使用 F#,由于我是 F# 新手,所以我只是选择了一个常见的例子,然后继续实现了一个阶乘函数:

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。

但是,如果我在发布模式下编译这两个 sn-ps 并比较生成的 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 

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

【问题讨论】:

    标签: f# visual-studio-2010 cil


    【解决方案1】:

    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。

    【讨论】:

    • 好吧,我在 VS2010 中使用默认的“发布”构建配置文件,所以我假设我实际上是在发布模式下构建。输出窗口显示“构建开始:项目:FSharpLib,配置:释放任何 CPU”。如果我改为“调试”,我会得到完全不同的 MSIL。
    • 我正在使用 F# 1.9.7.8 和命令行进行测试。如果我不通过 /debug,我就不会得到 nop。
    • 我用命令行更新了编译问题,并尝试直接使用 fsc.exe 进行编译。结果相同。根据输出窗口,VS2010b2 不附带最新的编译器,因为它报告的版本号为 F# Version 1.9.7.4。会不会有区别?
    • 您发布的命令行包含--debug:pdbonly,这会触发包含nop。
    • NOP IL 指令是 JITted 到 NOP 机器代码指令,还是真正的无操作并被忽略?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多