Members

Technology Zones

Articles

Hosted By

MaximumASP

Info

Rated
Read 6,065 times

Contents

Related Categories

Common Intermediate Language - Implementing a Loop

gbarnett

Implementing a Loop

If anyone has ever touched any assembler then you will be aware of opcodes that do comparisons, this includes assembly languages like MIPS.

I present first the C# code of the for loop.

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(i);
            }
        }
    }
}

Now here is my implementation of the same using MSIL.

.assembly extern mscorlib {}

.assembly lesthan 
{ 
    .ver 1:0:0:0
}

.module LessThan.exe

.method static void main()
{
    .entrypoint
    .maxstack 2
    
    .locals init (int32, int32)

    ldc.i4 10
    stloc.0
    ldc.i4 0
    stloc.1
    
    Expr:
        ldloc.1
        ldloc.0
        blt Body
        ret
        
    Body:
        ldloc.1
        call void [mscorlib]System.Console::WriteLine(int32)
        ldc.i4 1
        ldloc.1
        add
        stloc.1
        br Expr
}

This code is pretty easy to disassemble.  I create a branch block that defines the expression (.Expr) i (where i is the current value in mem location 1) < 10 (location 0) if this expression is true then we branch to .Body which basically prints out the current value of i as well as incrementing that value by 1, we then check the expression holds true again - if so we go again, if not we return power to the caller.

There is a key difference between what the C# compiler will give you and what I have given you, the first is that I have used fairly meaningful branch names, and the second is that I have decided to initialize two local variables holding both the counter (initially 0) and the max (10) - the C# compiler in the above code will only create a local variable for i, 10 will be loaded onto the stack when required.

The C# compiler will honour the fact that 10 is not assigned to any variable so the below is a more accurate representation of what the compiler will generate us, i is a local variable where as the integer 10 is loaded onto the stack when the expression is evaluated.

.assembly extern mscorlib {}

.assembly lesthanC 
{ 
    .ver 1:0:0:0
}

.module LessThanC.exe

.method static void main()
{
    .entrypoint
    .maxstack 2 
    
    .locals init (int32)
    
    ldc.i4 0
    stloc.0
    
    Expr:
        ldloc.0
        ldc.i4 10
        blt Body
        ret
        
    Body:
        ldloc.0
        call void [mscorlib]System.Console::WriteLine(int32)
        ldc.i4 1
        ldloc.0
        add
        stloc.0
        br Expr
}

Here is in fact what the C# compiler generates for us (release mode, code optimization).

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       20 (0x14)
  .maxstack  2
  .locals init ([0] int32 i)
  IL_0000:  ldc.i4.0
  IL_0001:  stloc.0
  IL_0002:  br.s       IL_000e
  IL_0004:  ldloc.0
  IL_0005:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_000a:  ldloc.0
  IL_000b:  ldc.i4.1
  IL_000c:  add
  IL_000d:  stloc.0
  IL_000e:  ldloc.0
  IL_000f:  ldc.i4.s   10
  IL_0011:  blt.s      IL_0004
  IL_0013:  ret
} // end of method Program::Main

Have a look at the MSIL.  We first store the int 0 onto the stack, then we branch to IL_000e - here we check that i < 10 if so we branch to IL_0004 where we load the value of i onto the stack then print it out to the console window, we then push i onto the stack, and then push 1 onto the stack then add the two popping them both off the stack and store the result of the addition we then perform the i < 10 expression again until it evaluates to false then we return power to the caller.

My name is Granville Barnett I have been a programmer now for quite some time mainly focusing on .NET technologies (C#), C++ and general research (algorithms, compilers etc)

Comments