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

Phi*_* P. 9 f#

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

我有一个开箱即用的VS 2010 F#项目,所有默认设置都针对.NET 4.0.

F#代码是这样的:

let test(a:int, b:int, c:int) = min a (min b c)
Run Code Online (Sandbox Code Playgroud)

当我编译它以供发布时,生成的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
Run Code Online (Sandbox Code Playgroud)

我的.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>
Run Code Online (Sandbox Code Playgroud)

现在,如果我注释掉线<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
Run Code Online (Sandbox Code Playgroud)

.locals行还有一个微妙的不同:

.locals init ([0] int32 V_0)
Run Code Online (Sandbox Code Playgroud)

VS

.locals init (int32 V_0)
Run Code Online (Sandbox Code Playgroud)

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

问题:

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

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

PS.这里这里有关于SO的相关问题,但是那里有所有的答案

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

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

Tom*_*cek 8

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

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

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