Why does this very simple C# method produce such illogical CIL code?

I've been digging into IL recently, and I noticed some odd behavior of the C# compiler. The following method is a very simple and verifiable application, it will immediately exit with exit code 1:

static int Main(string[] args)
    return 1;

When I compile this with Visual Studio Community 2015, the following IL code is generated (comments added):

.method private hidebysig static int32 Main(string[] args) cil managed
  .maxstack  1
  .locals init ([0] int32 V_0)     // Local variable init
  IL_0000:  nop                    // Do nothing
  IL_0001:  ldc.i4.1               // Push '1' to stack
  IL_0002:  stloc.0                // Pop stack to local variable 0
  IL_0003:  br.s       IL_0005     // Jump to next instruction
  IL_0005:  ldloc.0                // Load local variable 0 onto stack
  IL_0006:  ret                    // Return

If I were to handwrite this method, seemingly the same result could be achieved with the following IL:

.method static int32 Main()
  ldc.i4.1               // Push '1' to stack
  ret                    // Return

Are there underlying reasons that I'm not aware of that make this the expected behaviour?

Or is just that the assembled IL object code further optimized down the line, so the C# compiler does not have to worry about optimization?

Jon Skeet

The output you've shown is for a debug build. With a release build (or basically with optimizations turned on) the C# compiler generates the same IL you'd have written by hand.

I strongly suspect that this is all to make the debugger's work easier, basically - to make it simpler to break, and also see the return value before it's returned.

Moral: when you want to run optimized code, make sure you're not asking the compiler to generate code that's aimed at debugging :)


See more on this question at Stackoverflow