tags:

views:

195

answers:

2

I'm trying to understand how a while loop looks in IL. I have written this C# function:

    static void Brackets()
    {
        while (memory[pointer] > 0)
        {
            // Snipped body of the while loop, as it's not important
        }
    } 

The IL looks like this:

.method private hidebysig static void  Brackets() cil managed
{
  // Code size       37 (0x25)
  .maxstack  2
  .locals init ([0] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  br.s       IL_0012
  IL_0003:  nop
  // Snipped body of the while loop, as it's not important
  IL_0011:  nop
  IL_0012:  ldsfld     uint8[] BFHelloWorldCSharp.Program::memory
  IL_0017:  ldsfld     int16 BFHelloWorldCSharp.Program::pointer
  IL_001c:  ldelem.u1
  IL_001d:  ldc.i4.0
  IL_001e:  cgt
  IL_0020:  stloc.0
  IL_0021:  ldloc.0
  IL_0022:  brtrue.s   IL_0003
  IL_0024:  ret
} // end of method Program::Brackets

For the most part this is really simple, except for the part after cgt.

What I don't understand is the local [0] and the stloc.0/ldloc.0. As far as I see it, cgt pushes the result to the stack, stloc.0 gets the result from the stack into the local variable, ldloc.0 pushes the result to the stack again and brtrue.s reads from the stack.

What is the purpose of doing this? Couldn't this be shortened to just cgt followed by brtrue.s?

+3  A: 

Try compiling with optimizations.

Ben Voigt
That actually "fixes" it - so is it only there for Debug/Breakpoint purposed? Just curious why it's generated in the first place.
Michael Stum
I'd guess the two instructions which cancel each other come from different parts of the compiler. I could definitely imagine that all while loops initially get converted to nop (as a jump target) .... ldloc.<i> brtrue.s (addr of nop). Seems sane enough if you know the optimizer will remove the unneeded nop and so on in the next pass.
Ben Voigt
+3  A: 

That is a debug build (from the nop). All bets are off, but it looks like it is simply introducing a bool variable for simplicity:

    goto testforexit;
body:
    ..
testforexit:
    bool tmp = memory[pointer] > 0;
    if(tmp) goto body;

Build in release with optimisations enabled, which should remove such variables.

Marc Gravell