March 1, 2021
Hot Topics:

.NET Under the Hood: a Little ILDASM

  • By Kate Gregory
  • Send Email »
  • More Articles »


ILDASM is the Intermediate Language Disassembler. If you use a "Visual Studio. NET Command Prompt" (Start, Programs, Visual Studio .NET, Visual Studio .NET Tools, Visual Studio .NET Command Prompt), the environment variables will be set properly to enable you to use ILDASM and other handy utilities. From this command prompt, you can cd to the folder with an exe or dll file (a managed one, with IL in it) and take a look at the IL, like this:

ildasm simplec++.exe

The first screen shows the namespaces, classes, and modules in the assembly, along with the functions in each class or module. Here's SimpleC++ in ILDASM:

Click here for a larger image.

The small yellow S in the pink square next to main and _mainCRTStartup indicate that they are static functions. Double-clicking the main expands the IL for the function. Here it is:

.method public static int32
        .CallConvCdecl) main() cil managed
  .vtentry 1 : 1
  // Code size       36 (0x24)
  .maxstack  2
  .locals ([0] int32 i)
  IL_0000:  ldc.i4.0
  IL_0001:  stloc.0
  IL_0002:  br.s       IL_0008
  IL_0004:  ldloc.0
  IL_0005:  ldc.i4.1
  IL_0006:  add
  IL_0007:  stloc.0
  IL_0008:  ldloc.0
  IL_0009:  ldc.i4.s   10
  IL_000b:  bge.s      IL_0020
  IL_000d:  ldloca.s   i
  IL_000f:  ldobj      [mscorlib]System.Int32
  IL_0014:  box        [mscorlib]System.Int32
  IL_0019:  call       void [mscorlib]System.Console::
  IL_001e:  br.s       IL_0004
  IL_0020:  ldc.i4.0
  IL_0021:  br.s       IL_0023
  IL_0023:  ret
} // end of method 'Global Functions'::main

Now, this may look intimidating, but actually IL is human-readable. It helps if you've ever worked with assembly language, but even if you haven't, you can read this. There's a tutorial in the online help in Visual Studio that explains, for example, that ldc.i4.0 loads a four-byte zero onto the stack and stloc.0 stores the current stack value in local variable 0. It's called ILDASM Tutorial and should be easily reachable from the help index.

Here's the IL generated from the C#:

.method private hidebysig static void  Main(string[] args)
        cil managed
  .custom instance void [mscorlib]System.
          STAThreadAttribute::.ctor() = ( 01 00 00 00 )
  // 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::
  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 Class1::Main

This is shorter, for one thing, and it doesn't have the boxing. The for loop works differently too: The C++ checks whether i is greater than or equal to 10 and, if so, jumps past the whole loop using bge. The C# initializes i to zero, then jumps to the bottom of the loop, which checks to see whether i is less than 10 and, if so, jumps back to the top of the loop using blt. Neither of these approaches is necessarily better, but it's interesting that two superficially similar languages produce such different IL for such a simple example.

What about VB? Well, here's the IL generated from the VB code:

.method public static void  Main() cil managed
  .custom instance void [mscorlib]System.STAThreadAttribute:: _
          .ctor() = ( 01 00 00 00 ) 
  // Code size       22 (0x16)
  .maxstack  2
  .locals init ([0] int32 i)
  IL_0000:  nop
  IL_0001:  ldc.i4.0
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  call       void [mscorlib]System.Console:: _
  IL_0009:  nop
  IL_000a:  nop
  IL_000b:  ldloc.0
  IL_000c:  ldc.i4.1
  IL_000d:  add.ovf
  IL_000e:  stloc.0
  IL_000f:  ldloc.0
  IL_0010:  ldc.i4.s   9
  IL_0012:  ble.s      IL_0003
  IL_0014:  nop
  IL_0015:  ret
} // end of method Module1::Main

This code is strangely full of nop—no operation—statements, but you can see that it is very similar to the C# code. It uses ble instead of blt because VB loops continue when the index is equal to the limit—less than or equal to—but the C# code used a less-than in the limit test. (If you mess around with the loop limits, going from 10 to 9 or from 3 to 9, for example, you'll see code that looks even more like the C# code.)

The Bottom Line

What's the difference between C# and C++? They both have semicolons and brace brackets, after all. Well, that may be so, but they're very different languages. C# is far more like VB than it is like C++. And, taking a look under the hood at the IL you get from simple little applications is a good way to reveal that. What's more, because it's the IL that actually executes, any talk of faster or slower execution times needs to be underpinned with an understanding of the IL that is generated. ILDASM is a good way for you to understand what your code actually produces. Play a little!

About the Author

Kate Gregory is a founding partner of Gregory Consulting Limited (www.gregcons.com). In January 2002, she was appointed MSDN Regional Director for Toronto, Canada. Her experience with C++ stretches back to before Visual C++ existed. She is a well-known speaker and lecturer at colleges and Microsoft events on subjects such as .NET, Visual Studio, XML, UML, C++, Java, and the Internet. Kate and her colleagues at Gregory Consulting specialize in combining software develoment with Web site development to create active sites. They build quality custom and off-the-shelf software components for Web pages and other applications. Kate is the author of numerous books for Que, including Special Edition Using Visual C++ .NET.

# # #

Page 2 of 2

This article was originally published on March 6, 2003

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date