Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does F# compiler generates IL NOP even in release builds?

Tags:

f#

I've just stumbled across this and I'm a bit puzzled.

I have an out-of-the-box VS 2010 F# project, with all default settings, targeting .NET 4.0.

The F# code is like this:

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

When I compile it for release, the generated IL contains some strange NOP instructions scattered around. Like this:

The generated IL for this (with all default settings):

.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

My .fsproj project configuration is:

  <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>

Now, if I comment out line <DebugType>pdbonly</DebugType>, the NOP instructions disappear. But of course so does the PDB file!

.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

There is also a subtle different in the .locals line:

.locals init ([0] int32 V_0)

vs

.locals init (int32 V_0)

When I tried C# compiler, it generates NOP instructions only in the debug builds, but these seem to go away in release builds, even when PDB files are included using <DebugType>pdbonly</DebugType>.

Questions:

  1. Why are NOP instructions generated in F# in release build when PDB files are included, when C# seems to be able to avoid this.

  2. Is there any way to get rid of those NOP, but still have the PDB files?

PS. There are related questions on SO here and here, but all the answers there say

you are compiling in debug mode, if you compile in release mode, the NOP goes away

which contradicts my experience with F# compiler as demonstrated.

like image 825
Philip P. Avatar asked Apr 13 '12 10:04

Philip P.


People also ask

What does f apostrophe mean?

This formula, or new function, is called the derivative of the original function. When we find it we say that we are differentiating the function. The derivative of f(x) is written using an apostrophe after the f. The notation is f´(x) or y´ The notation dy/dx is also commonly used.

What does f mean in algebra?

A function is most often denoted by letters such as f, g and h, and the value of a function f at an element x of its domain is denoted by f(x); the numerical value resulting from the function evaluation at a particular input value is denoted by replacing x with this value; for example, the value of f at x = 4 is ...

Is f x the same as Y?

An equation is a relationship between variables. Yes, y=x+3 and f(x)= x+3 yield the same results, which is why we teachers always tell you to remember that 'y and f(x) are the same thing'.


1 Answers

I don't exactly know how this works internally in the F# compiler (somebody from the F# team may have a better answer), but my guess is that generating nop instructions is just a fairly easy way to generate locations that can be referred to from the pdb file.

The pdb file needs to specify some IL range for expression in the code where you can place a breakpoint - and this is the case in both Debug and Release mode. This means that if you place a breakpoint somewhere, there needs to be a corresponding location in the IL. However, if there is no actual instruction, corresponding to the source code location, the compiler needs to insert something - so it adds nop.

In Release mode, the F# compiler does more optimizations, but if you want pdb files, it still needs to provide location for all source code locations. This might not be needed in C#, because C# maps more closely to the source IL, but it might be harder to avoid this in F#.

like image 139
Tomas Petricek Avatar answered Oct 22 '22 06:10

Tomas Petricek