.NET internals: Stack-Oriented .NET IL

.NET IL supports a stack-oriented and object-oriented approach. In our previous tutorial, we talked about the essentials of .NET IL and how to compile our code.

This article explores a basic IL program, providing a glimpse into how code interacts with the .NET Framework. While you wouldn't typically write IL code directly, understanding it can enhance your grasp of the .NET compilation process. Developers typically utilize higher-level languages like C# for application development, understanding IL can significantly enhance your grasp of the .NET compilation process and provide a unique perspective on how programs are executed.

IL, also known as Common Intermediate Language (CIL), acts as an intermediary stage between human-readable source code (like C#) and machine-specific instructions a computer understands. This lets various .NET languages compile down to a common format, enabling them to run on different systems.

Today’s focus is to understand stack-oriented .NET IL.

.assembly extern mscorlib {}
.assembly MyApp {}
.module MyApp.exe

.class public Program extends [mscorlib]System.Object
{
.method public static void MyEntryPoint(string[] args) cil managed
{
   .entrypoint
   .locals init([0] int32 Result)
    ldc.i4 0x34
    ldc.i4 0x32
    add
    stloc.0
    ldloc.0
    call void [mscorlib]System.Console::WriteLine(int32)
    ret
  }
}

Let's dissect the provided code line by line. The first three lines (.assembly directives) define external assemblies (pre-built libraries) used in the program. mscorlib is a core .NET library providing essential functionalities.

PS: Checkout our repo for the source code.

The subsequent line (.module) declares our program itself. It assigns the name MyApp.exe to the module, essentially giving our program an identity within the .NET environment. The following section, denoted by the .class directive, defines a public class named Program. This class acts as the blueprint for our program's structure and behavior. The inheritance clause (extends [mscorlib]System.Object) signifies that the Program class inherits properties and methods from the fundamental System.Object class found within the mscorlib assembly. This inheritance establishes a foundational structure upon which we build the functionalities of our program.

The definition introduced by the .method directive brings a public static function named MyEntryPoint into focus. This function serves as the entry point for our program's execution. It possesses the capability to accept an array of strings (string[] args) as input. This array is commonly used for capturing command-line arguments that might be provided when running the program from the console. The cil managed attribute attached to the method declaration specifies that the code within this function is written in Common Intermediate Language and is consequently managed by the Common Language Runtime (CLR). The CLR acts as the core execution environment of .NET, responsible for loading, managing, and executing .NET programs.

The next two lines (ldc.i4 0x34 and ldc.i4 0x32) utilize the ldc.i4 instruction, which stands for "load constant (integer 4-byte)". These lines essentially push constant values onto the execution stack, a temporary storage location within the CLR. The values being pushed are represented in hexadecimal format (0x34 translates to decimal 52 and 0x32 translates to decimal 50). The subsequent line (add) retrieves the two values from the execution stack, performs an addition operation on them, and stores the resulting value (which is the sum of 52 and 50) within the Result variable we declared earlier.

The line ldloc.0 employs the ldloc instruction, specifically ldloc.0 which translates to "load local (index 0)". This instruction retrieves the value stored in the local variable Result (which holds the sum we calculated) and pushes it onto the execution stack. The line call void [mscorlib]System.Console::WriteLine(int32) performs a function call. It invokes the WriteLine method from the System.Console class, which resides within the mscorlib assembly.