我偶然发现了这一点,我有点困惑。
我有一个开箱即用的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文件。
问题:
答案 0 :(得分:8)
我不完全知道这在F#编译器内部是如何工作的(来自F#团队的人可能有更好的答案),但我的猜测是生成nop
指令只是一种相当简单的生成方式可以从pdb
文件中引用的位置。
pdb
文件需要在可以放置断点的代码中为表达式指定一些IL范围 - 这在Debug和Release模式下都是如此。这意味着如果在某处放置断点,则需要在IL中有相应的位置。但是,如果没有与源代码位置对应的实际指令,则编译器需要插入一些内容 - 因此它会添加nop
。
在发布模式下,F#编译器会进行更多优化,但如果您需要pdb
个文件,则仍需要为所有源代码位置提供位置。在C#中可能不需要这样做,因为C#更紧密地映射到源IL,但在F#中可能更难避免这种情况。