Home About Eric Topics SourceGear

2023-01-18 09:00:00

Native AOT Overview

This is part of a series on Native AOT.
Top -- Next

In typical .NET development, C# code is compiled to an intermediate language (IL) which is then executed by the Common Language Runtime (CLR). And when we say "executed", we mean that the IL is compiled to native code immediately before it is needed, by a Just in Time (JIT) compiler.

This is how .NET has worked since 2002.

More recently, we are also seeing a different approach, where .NET code is compiled Ahead of Time (AOT), directly to native code, skipping the IL step. The resulting native code doesn't need a runtime (or more precisely, it needs something like a runtime, but which is different and much smaller than the CLR).

One of the early efforts in this area happened with Mono, and the necessity that mothered that invention was iOS, which does not allow JIT compilation for security reasons. So in order to support its strategy of .NET for mobile, Xamarin (later acquired by Microsoft) implemented AOT compilation. The Xamarin toolchain was "ahead of its time" (cheesy pun intended, sorry, not sorry) in its ability to take C# code and create an iOS application that had no JIT.

At Microsoft, a similar effort was underway, but it was based on the .NET Core runtime instead of Mono. This project was called CoreRT, and for several years, it was considered to be "experimental".

Today, CoreRT has been released as part of .NET 7, and the feature is called Native AOT.


As mentioned above, one reason for AOT is the need to use .NET on platforms where dynamic code generation is not permitted. The early motivating example was iOS, but WebAssembly has similar constraints.

That said, Microsoft clearly has aspirations for this feature that go beyond environments where JIT is not allowed. AOT has other benefits.

Inherent Limitations

The notion of AOT has certain inherent limitations which are suggested by the name itself. Things need to be done Ahead of Time.

With Native AOT, we are building native code for use with a traditional linker (used by languages such as C++), so we have to follow the rules of that linker. Everything we need must get resolved up front. Once the linker is done, the cockpit door is closed, and nobody else is getting on the plane.

C# and .NET support dynamic code generation at runtime, but those features are not compatible with AOT.

Current limitations

The current implementation of Native AOT is amazing, but it also has plenty of room for future growth. For example, as of .NET 7:

In addition, a lot of C# code has been written in the last 20 years, and some of it uses features that are not AOT compatible. We should not expect that we can recompile every .NET program unchanged with Native AOT and magically have it Just Work. In some cases, additional effort will be required to make things AOT-compatible.


Still, Native AOT as shipped in .NET 7 is really impressive, and there are plenty of indications that the feature is going to keep getting better in future releases.