views:

50

answers:

1

I am no IL master of any sort, I just use it sometimes to check what the compiler makes of the code that I write. One thing that I have been wondering about is why .maxstack gets the value it gets sometimes. Consider the following class:

public class Sample
{
    public void SomeMethod(){}
}

Then, I have a program like this:

private static void Main(string[] args)
{
    Sample sample = new Sample();
    sample.SomeMethod();      
}

The above code gives the following IL (Release compiled):

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       13 (0xd)
  .maxstack  1
  .locals init ([0] class ConsoleApplication1.Sample sample)
  IL_0000:  newobj     instance void ConsoleApplication1.Sample::.ctor()
  IL_0005:  stloc.0
  IL_0006:  ldloc.0
  IL_0007:  callvirt   instance void ConsoleApplication1.Sample::SomeMethod()
  IL_000c:  ret
} // end of method Program::Main

Now, if I change the program code into this:

private static void Main(string[] args)
{
    new Sample().SomeMethod();  
}

...it results in the following IL code:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       11 (0xb)
  .maxstack  8
  IL_0000:  newobj     instance void ConsoleApplication1.Sample::.ctor()
  IL_0005:  call       instance void ConsoleApplication1.Sample::SomeMethod()
  IL_000a:  ret
} // end of method Program::Main

The second IL code is shorter, which was expected. But what gets me a bit curious is why .maxstack i 8 in the second code sample, but 1 in the first? Why does the second code lead to the system reserving a larger stack for the operation?

+3  A: 

The binary representation of a method header has a "tiny format" and a "fat format". The tiny header takes fewer bytes and can be used as long as the following conditions are met:

  • Max stack <= 8
  • No exception handling
  • No local variables
  • Code size < 64 bytes

Your change allowed the compiler to use this form, and when a tiny header is encountered it is always assumed to use a max stack of 8.

Reference is ECMA-335 §25.4.2

On a side note Of particular interest to me is the fact that in a release build (per your note in the OP) they produced different code, where the shortened form is both smaller and faster. What version of C# are you using? I would expect later versions to take advantage of this obvious optimization:

  • The local variable could be removed without changing the semantics.
  • When the local variable is not present, the compiler knows the concrete type of the value is exactly Sample, so even though SomeMethod is virtual it can call it directly with the call instruction.
280Z28