Common Intermediate Language
Common Intermediate Language (CIL, pronounced either "sil" or "kil") (formerly called Microsoft Intermediate Language or MSIL) is the lowest-level human-readable programming language in the Common Language Infrastructure and in the .NET Framework. Languages which target the .NET Framework compile to CIL, which is assembled into bytecode. CIL is an object-oriented assembly language, and is entirely stack-based. It is executed by a virtual machine.
CIL was originally known as Microsoft Intermediate Language (MSIL) during the beta releases of the .NET languages. Due to standardization of C# and the Common Language Infrastructure, the bytecode is now officially known as CIL. Because of this legacy, CIL is still frequently referred to as MSIL, especially by long-standing users of the .NET languages.
General information
During compilation of .NET programming languages, the source code is translated into CIL code rather than platform or processor-specific object code. CIL is a CPU- and platform-independent instruction set that can be executed in any environment supporting the .NET framework (either the .NET runtime on Microsoft Windows operating system, or the independently derived Mono, which also works on Linux or Unix-based operating systems). CIL code is verified for safety during runtime, providing better security and reliability than natively compiled binaries.
Bytecode instructions
CIL bytecode has instructions for the following groups of tasks:
- Load and store
- Arithmetic
- Type conversion
- Object creation and manipulation
- Operand stack management (push / pop)
- Control transfer (branching)
- Method invocation and return
- Throwing exceptions
- Monitor-based concurrency
Just-in-time compilation
Just-in-time compilation involves turning the byte-code into code immediately executable by the CPU. The conversion is performed gradually during the program's execution. JIT compilation provides environment-specific optimization, runtime type safety, and assembly verification. To accomplish this, the JIT compiler examines the assembly metadata for any illegal accesses and handles violations appropriately.
Sample Code
Below is a basic Hello, World program written in CIL. It will display the string "Hello, world!".
.assembly Hello {}
.method public static void Main() cil managed
{
.entrypoint
.maxstack 1
ldstr "Hello, world!"
call void [mscorlib]System.Console::WriteLine(string)
ret
}
The following code is more complex in number of opcodes.
This code can also be compared with the corresponding code in the article about Java Bytecode.
static void Main(string[] args)
{
outer:
for (int i = 2; i < 1000; i++)
{
for (int j = 2; j < i; j++)
{
if (i % j == 0)
goto outer;
}
Console.WriteLine(i);
}
}
In IL syntax it looks like this:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init (int32 V_0,
int32 V_1)
IL_0000: ldc.i4.2
stloc.0
br.s IL_001f
IL_0004: ldc.i4.2
stloc.1
br.s IL_0011
IL_0008: ldloc.0
ldloc.1
rem
brfalse.s IL_0000
ldloc.1
ldc.i4.1
add
stloc.1
IL_0011: ldloc.1
ldloc.0
blt.s IL_0008
ldloc.0
call void [mscorlib]System.Console::WriteLine(int32)
ldloc.0
ldc.i4.1
add
stloc.0
IL_001f: ldloc.0
ldc.i4 0x3e8
blt.s IL_0004
ret
}
This is just a representation of how IL looks like near VM-level. When compiled the methods are stored in tables and the instructions are stored as bytes inside the assembly, which is a Portable Executable-file (PE).
Compiling CIL
CIL can be very easily compiled by anyone who has the .NET framework installed on their computer, as it is distributed with a tool called the ILAsm (IL Assembler). The ILAsm executable can be found in the WINDOWS folder, on the root drive. The specific location of it may vary with the version of the Framework you have, but will be in at least one of the version folders in C:\WINDOWS\Microsoft.NET\Framework.
To compile a program with the ILAsm on a computer running Windows, simply execute this command in the Command Prompt:
ilasm filename.il /exe
This will, literally, compile the filename referenced into a .exe file. You can change it to become a DLL, for example, by changing the last attribute to dll instead of exe.
Be aware that you will have to give the full reference of the filename while running in the Folder that contains ILAsm.exe, or may need to copy the ILAsm to the same folder as the IL file, and run the command in that folder. If you intend to use the ILAsm on a regular or professional basis you may want to add the ILAsm folder to one of the recognised paths in the DOS prompt. This can be done through the control panel.
The ILAsm.exe is also distributed with Mono for Linux or Mac computers.
Native image generator compilation
The native image generator (NGEN) produces a native binary image for the current environment (i.e; operating systems). This eliminates the JIT overhead at the expense of portability; whenever an NGEN-generated image is run in an incompatible environment, .NET framework automatically reverts to using JIT. Once NGEN is run against an assembly, the resulting native image is placed into the Native Image Cache for use by all other .NET assemblies. This makes it possible, for example, to use NGEN to process .NET assemblies at installation time, saving processor time later on, when the end-user invokes the application on their system.
NGEN is intended to make the assembly execute faster by removing the JIT compilation process at runtime, but this does not always improve performance because some optimizations can be done only by a JIT compiler (e.g., if the JIT compiler knows that the code is already running with full trust, it can skip certain expensive security checks). Because of this fact, it makes sense to use NGEN only after benchmarking the application performance before and after it.
Metadata
.NET records information about compiled classes as Metadata. Like the type library in the Component Object Model, this enables applications to support and discover the interfaces, classes, types, methods, and fields in the assembly. The process of reading such metadata is called reflection.
Executing CIL
- Source code is converted to Common Intermediate Language, .NET’s equivalent to Assembly language for a CPU.
- CIL is then assembled into bytecode and a .NET assembly is created.
- Upon execution of a .NET assembly, its bytecode is passed through the Common Language Runtime's JIT compiler to generate native code. (NGEN compilation eliminates this step at run time.)
- The native code is executed by the computer's processor.