为什么F#编译器即使在发布版本中也会生成IL NOP?

时间:2012-04-13 10:28:45

标签: f#

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

我有一个开箱即用的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>

现在,如果我注释掉<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)

VS

.locals init (int32 V_0)

当我尝试使用C#编译器时,它只生成NOP指令 调试构建,但这些似乎在发布版本中消失,即使在 使用<DebugType>pdbonly</DebugType>包含PDB文件。

问题:

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

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

  3. PS。有关SO here的相关问题 和here, 但那里的所有答案都说

      

    你是在调试模式下编译,如果你在发布模式下编译,NOP就会消失

    与我对F#编译器的经验相矛盾。

1 个答案:

答案 0 :(得分:8)

我不完全知道这在F#编译器内部是如何工作的(来自F#团队的人可能有更好的答案),但我的猜测是生成nop指令只是一种相当简单的生成方式可以从pdb文件中引用的位置。

pdb文件需要在可以放置断点的代码中为表达式指定一些IL范围 - 这在Debug和Release模式下都是如此。这意味着如果在某处放置断点,则需要在IL中有相应的位置。但是,如果没有与源代码位置对应的实际指令,则编译器需要插入一些内容 - 因此它会添加nop

在发布模式下,F#编译器会进行更多优化,但如果您需要pdb个文件,则仍需要为所有源代码位置提供位置。在C#中可能不需要这样做,因为C#更紧密地映射到源IL,但在F#中可能更难避免这种情况。