9

我刚刚偶然发现了这一点,我有点困惑。

我有一个开箱即用的 VS 2010 F# 项目,具有所有默认设置,面向 .NET 4.0。

F# 代码是这样的:

let test(a:int, b:int, c:int) = min a (min b c)

当我编译它以发布时,生成的 IL 包含一些NOP 分散的奇怪指令。像这样:

为此生成的 IL(使用所有默认设置):

.method public static int32  test(int32 a,
                                  int32 b,
                                  int32 c) cil managed
{
  // Code size       20 (0x14)
  .maxstack  4
  .locals init ([0] int32 V_0)

  // HERE
  IL_0000:  nop

  IL_0001:  ldarg.1
  IL_0002:  ldarg.2
  IL_0003:  bge.s      IL_0009
  IL_0005:  ldarg.1

  // HERE
  IL_0006:  nop

  IL_0007:  br.s       IL_000b
  IL_0009:  ldarg.2

  // HERE
  IL_000a:  nop

  IL_000b:  stloc.0
  IL_000c:  ldarg.0
  IL_000d:  ldloc.0
  IL_000e:  bge.s      IL_0012
  IL_0010:  ldarg.0
  IL_0011:  ret
  IL_0012:  ldloc.0
  IL_0013:  ret
} // end of method Module1::test

我的.fsproj项目配置是:

  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <Tailcalls>true</Tailcalls>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <WarningLevel>3</WarningLevel>
    <DocumentationFile>bin\Release\TestFsIL.XML</DocumentationFile>
  </PropertyGroup>

现在,如果我注释掉 line <DebugType>pdbonly</DebugType>NOP说明就会消失。当然,PDB 文件也是如此!

.method public static int32  test(int32 a,
                                  int32 b,
                                  int32 c) cil managed
{
  // Code size       17 (0x11)
  .maxstack  4
  .locals init (int32 V_0)
  IL_0000:  ldarg.1
  IL_0001:  ldarg.2
  IL_0002:  bge.s      IL_0007
  IL_0004:  ldarg.1
  IL_0005:  br.s       IL_0008
  IL_0007:  ldarg.2
  IL_0008:  stloc.0
  IL_0009:  ldarg.0
  IL_000a:  ldloc.0
  IL_000b:  bge.s      IL_000f
  IL_000d:  ldarg.0
  IL_000e:  ret
  IL_000f:  ldloc.0
  IL_0010:  ret
} // end of method Module1::test

这条线也有一个微妙的不同.locals

.locals init ([0] int32 V_0)

对比

.locals init (int32 V_0)

当我尝试 C# 编译器时,它NOP仅在调试版本中生成指令,但这些似乎在发布版本中消失了,即使使用<DebugType>pdbonly</DebugType>.

问题:

  1. 当包含 PDB 文件时,为什么在 F# 中在发布版本中生成 NOP 指令,而 C# 似乎能够避免这种情况。

  2. 有没有办法摆脱那些 NOP,但仍然有 PDB 文件?

PS。这里这里有关于 SO 的相关问题,但那里的所有答案都说

您正在调试模式下编译,如果您在发布模式下编译,NOP则消失

这与我展示的 F# 编译器的经验相矛盾。

4

1 回答 1

8

我不完全知道这在 F# 编译器内部是如何工作的(F# 团队的某个人可能有更好的答案),但我的猜测是生成nop指令只是生成可以从pdb文件。

pdb文件需要在代码中为表达式指定一些 IL 范围,您可以在其中放置断点 - 调试和发布模式都是这种情况。这意味着如果您在某处放置断点,则 IL 中需要有相应的位置。但是,如果没有实际的指令,对应于源代码位置,编译器需要插入一些东西——所以它添加了nop.

在 Release 模式下,F# 编译器做了更多的优化,但如果你想要pdb文件,它仍然需要提供所有源代码位置的位置。这在 C# 中可能不需要,因为 C# 更接近于源 IL 映射,但在 F# 中可能更难避免这种情况。

于 2012-04-13T10:51:19.860 回答