Est. 2002 Advanced

MSIL / CIL

The portable, stack-based bytecode of the .NET platform — known informally as MSIL and standardized as Common Intermediate Language (CIL) — that every managed language compiles to before just-in-time or ahead-of-time compilation to native code.

Created by Microsoft (Common Language Runtime team), standardized by Ecma International and ISO/IEC

Paradigm Stack-based intermediate representation (bytecode) for managed code on the Common Language Infrastructure
Typing Static and strong via the Common Type System; supports verifiable type-safe code as well as unverifiable low-level code
First Appeared 2002 (with .NET Framework 1.0; previewed in beta as MSIL from 2000)
Latest Version Instruction set defined by ECMA-335, 6th edition (June 2012); continuously extended in practice by modern .NET runtimes (.NET 8, 2023)

MSIL / CIL is the intermediate bytecode that the entire .NET platform is built upon. When you compile a C#, F#, or Visual Basic .NET program, the compiler does not produce machine code for a particular processor. Instead it produces Intermediate Language — a compact, processor- and operating-system-neutral instruction set — and packages it together with descriptive metadata into an assembly. Only when the program actually runs does the Common Language Runtime (CLR) translate that IL into native instructions, either just-in-time (JIT) as methods are first called, or ahead-of-time (AOT) when the application is published. This two-stage model — compile once to portable IL, then compile again to native code per machine — is the defining characteristic of managed code on .NET.

One language, two names. “MSIL” and “CIL” refer to the same instruction set. Microsoft Intermediate Language was the name used during the .NET betas around 2000–2001. When the platform’s core was standardized as ECMA-335 in December 2001, the instruction set was formally christened Common Intermediate Language (CIL). The casual shorthand IL is used for both. The MSIL name lives on in tooling — the ilasm assembler, the ildasm disassembler, and the MSIL platform target in many build systems. This page treats MSIL and CIL as interchangeable, with a slight preference for the standardized name CIL.

History and Origins

CIL emerged from Microsoft’s effort, in the late 1990s, to build a managed application platform that became .NET. The platform was previewed publicly at the 2000 Professional Developers Conference, and the runtime’s bytecode at that stage was called MSIL. The ambitions behind it were deliberately broad: a single execution engine capable of hosting many programming languages, providing automatic memory management, enforcing type safety, and running code portably across machines without recompilation from source.

To position the platform as an open technology rather than a proprietary product, Microsoft — together with Intel, Hewlett-Packard, and others — submitted the core specifications to Ecma International. In December 2001 the Ecma General Assembly ratified the first edition of ECMA-335, the Common Language Infrastructure standard. Its five partitions define, among other things, the Common Type System, the file format of assemblies, the metadata tables, and — in Partition III — the CIL instruction set itself. Ecma submitted the work to ISO/IEC through the fast-track process, and it was published as ISO/IEC 23271 (the C# language being standardized in parallel as ISO/IEC 23270).

The first production release arrived with .NET Framework 1.0 on 13 February 2002, bundling the CLR, the base class libraries, and the IL tooling. The execution model it established — emit IL, ship IL, JIT-compile to native code at load time — is essentially the same one .NET uses more than two decades later.

Design Philosophy

CIL is a stack-based virtual machine language. Rather than referencing named registers, its instructions push operands onto an evaluation stack and pop results back off. To add two numbers, code loads each operand and then issues a single add instruction that consumes the top two stack slots and pushes the sum. This stack discipline keeps the instruction encoding small and keeps the IL independent of any real processor’s register file — leaving the JIT free to map the abstract stack onto the actual registers of an x86, x64, or ARM chip.

Several principles set CIL apart from a raw assembly language:

  • Typed, managed instructions. IL is not an untyped stream of bytes. It is bound to the Common Type System (CTS), and every assembly carries metadata describing its types, method signatures, and fields. That metadata is what makes garbage collection, reflection, and runtime security checks possible.
  • Verifiability. A well-defined subset of IL is verifiable: before executing it, the runtime can prove the code is type-safe and cannot corrupt memory. IL can also express unverifiable operations — raw pointers, unsafe code — but verifiability is the foundation of the safety guarantees managed code provides.
  • Language neutrality. The IL and CTS were designed so object-oriented, functional, and procedural languages could all target them. The Common Language Specification (CLS) defines a shared subset that guarantees interoperability between languages.
  • Deferred compilation. Because IL is lowered to native code only at run time — or, increasingly, ahead of time — the same assembly can run on different architectures, and the compiler can tune its output for the exact CPU it encounters.

Key Features

A handful of elements characterize the MSIL / CIL experience:

  • Assemblies and metadata. An assembly (a .dll or .exe) bundles IL with a self-describing metadata manifest. This is what enables reflection, late binding, and tools that inspect or rewrite code with no access to source.
  • ilasm and ildasm. The IL assembler and disassembler let developers read the IL emitted by any compiler — and even hand-author IL. Round-tripping an assembly through ildasm → edit → ilasm is a classic technique for studying and patching .NET binaries.
  • System.Reflection.Emit. These APIs let a program build and execute new IL methods at run time, the basis for many serializers, mocking libraries, and high-performance ORMs.
  • Runtime generics. Unlike platforms that erase generic type information, the CLR has, since version 2.0 (2005), represented generics directly in IL and metadata — so full type information survives to run time.

A small example — the body of a method that adds two integers, written in IL assembly:

.method public static int32 Add(int32 a, int32 b) cil managed
{
    .maxstack 2
    ldarg.0      // push the first argument
    ldarg.1      // push the second argument
    add          // pop two, push their sum
    ret          // return the top of the stack
}

Evolution

The formal standard has been stable — the most recent revision is the 6th edition of ECMA-335, published in June 2012 — but the practical IL ecosystem has evolved continuously alongside the runtimes that consume it:

  • CLR 2.0 (2005) added runtime generics, extending the IL metadata and instruction set.
  • Mono 1.0 (2004) demonstrated that the standardized IL could run on a wholly independent, open-source engine, executing the same assemblies produced for Microsoft’s runtime.
  • .NET Core 1.0 (2016) brought an open-source, cross-platform CLR (CoreCLR) and a re-engineered JIT (RyuJIT), running IL on Linux and macOS as well as Windows.
  • .NET 5 (2020) unified the .NET Framework and .NET Core lineages into a single product, all on the same IL execution model.
  • Native AOT (introduced for console apps in .NET 7, 2022) compiles IL ahead of time into a self-contained native binary, removing run-time JIT compilation entirely for scenarios that demand fast startup, a small footprint, or platforms that forbid generating code at run time.

Current Relevance

MSIL / CIL remains the indispensable substrate of one of the world’s most widely used development platforms. Every .NET application — ASP.NET Core services, desktop apps, and Unity games alike — exists as IL before it ever becomes native code. The platform’s dual JIT/AOT story means IL underpins both the dynamic, reflection-heavy workloads where just-in-time compilation excels and the constrained, startup-sensitive workloads now served by Native AOT and Unity’s IL2CPP.

IL is also a lively target for tooling. Decompilers such as ILSpy and the historical dnSpy, the Mono.Cecil rewriting library, and a range of profilers and obfuscators all operate at the IL level. Because IL is comparatively easy to read and to decompile back into high-level source, it sits at the center of both .NET reverse engineering and the obfuscation industry that exists to resist it.

Why It Matters

CIL proved, at scale, that a single intermediate language could be a genuine common target for many programming languages while still delivering type safety, garbage collection, and competitive performance through JIT compilation. Standardizing it through Ecma and ISO/IEC turned a vendor bytecode into an open specification with independent implementations — Mono most prominently — and helped make the broader CLI a durable, cross-platform foundation. Alongside the JVM’s bytecode, MSIL / CIL is one of the two most influential managed intermediate representations in computing, and the compile-to-portable-IL-then-JIT-or-AOT model it popularized remains a template that later runtimes and platforms continue to follow.


This entry covers the same instruction set as the MSIL page; “CIL” is its standardized name under ECMA-335, and the two terms are used interchangeably throughout the .NET ecosystem.

Timeline

2000
Microsoft previews the .NET platform at its Professional Developers Conference. In the early betas, the runtime's portable bytecode is named Microsoft Intermediate Language (MSIL).
2001
In December 2001 the Ecma General Assembly ratifies the 1st edition of the Common Language Infrastructure as ECMA-335. Partition III of the standard formally names the instruction set Common Intermediate Language (CIL).
2002
.NET Framework 1.0 ships (13 February 2002) with the Common Language Runtime, which loads CIL assemblies and JIT-compiles them to native code. The SDK includes the ilasm assembler and ildasm disassembler.
2003
ISO/IEC adopts the CLI through the fast-track process, publishing the standard as ISO/IEC 23271 (with the C# language as ISO/IEC 23270). .NET Framework 1.1 follows in April 2003.
2004
Mono 1.0 is released, the first major open-source, cross-platform implementation of the CLI that executes the same CIL assemblies as Microsoft's runtime.
2005
CLR 2.0 introduces generics implemented at the runtime level, extending the CIL instruction set and metadata to represent generic types and methods directly rather than erasing them.
2012
The 6th edition of ECMA-335 is published (June 2012), the most recent revision of the formal CLI / CIL standard.
2016
.NET Core 1.0 ships (June 2016) with the open-source CoreCLR and the RyuJIT compiler, running the same CIL bytecode on Windows, Linux, and macOS.
2020
.NET 5 (November 2020) unifies the .NET Framework and .NET Core lineages into a single product line, all built on the same CIL execution model.
2022
.NET 7 (November 2022) ships Native AOT for console applications, compiling CIL ahead-of-time into a self-contained native binary with no run-time JIT compilation of IL.

Notable Uses & Legacy

The .NET language compilers

C#, F#, and Visual Basic .NET all compile to CIL rather than to native code. The Roslyn compiler (C# and VB) and the F# compiler emit IL into assemblies that the runtime later JIT- or AOT-compiles, which is what lets these languages interoperate.

Unity and IL2CPP

The Unity game engine compiles a project's C# scripts to CIL, then its IL2CPP toolchain converts that IL into C++ that is compiled to native code for platforms — such as consoles and iOS — that require or prefer ahead-of-time compilation.

Run-time code generation

System.Reflection.Emit and expression trees let programs emit and execute CIL at run time. Serializers, ORMs, mocking frameworks, and dynamic proxies use this to build fast, specialized code paths on the fly.

Decompilers and assembly rewriters

Tools such as ILSpy, the historical dnSpy, ildasm, and the Mono.Cecil library read, edit, and decompile CIL — enabling debugging, profiling, obfuscation, security analysis, and reverse engineering of .NET assemblies.

Mono and the cross-platform CLI

Mono and its descendants execute the identical CIL assemblies produced on Windows, underpinning cross-platform development and, historically, the Xamarin mobile stack.

Language Influence

Influenced By

Java bytecode

Running Today

Run examples using the official Docker image:

docker pull mcr.microsoft.com/dotnet/sdk:8.0

Example usage:

docker run --rm -v $(pwd):/app -w /app mcr.microsoft.com/dotnet/sdk:8.0 dotnet build
Last updated: