Native Compilation – Why JIT when you can CodeGen!


Typical .NET Compilation, JIT Compilation and Execution Cycle

For 15 years, .NET apps are created with a compiler that emits intermediate code (IL). That IL is evaluated by a just-in-time compiler (JIT) that performs a native compilation of your application allowing it to run on the destination operating system that hosts the common language runtime (CLR).  Until recently, Microsoft-provided .NET tools only ran on Windows. If we want to shortcut this two-step compilation process and deliver a 100% native application on Windows, Mac, and Linux, we need an alternative to the CLR. The project that is aiming to deliver that solution with an ahead-of-time compilation process is called CoreRT.

CoreRT CodeGen Current and Planned

Direct Native-Compilation from .NET Source Code

The CoreRT compiler currently has the RyuJIT embedded at its heart to allow current environments where .NET is available to compile directly to native code.  You already know some of the places where we have the RyuJIT working nicely:  Windows Desktop, Windows Phone, MacOS, and several Linux Desktop distributions all use RyuJIT.  But what about those operating systems and environments where we don’t currently have .NET developers invested and working?

If I really want to write some C# code and have it “just work” on a new IoT device, I don’t have any options until the RyuJIT is capable of generating machine code that works with that processor and operating system.

One strategy we are investigating is the ability to transpile .NET code to C++ code that could be compiled with an appropriate C++ compiler for the target platform.  This strategy opens doors for hardware development because you would no longer need to wait for a Microsoft engineer to extend our compilers to support some new OS or new hardware in order to allow .NET developers to work with your product.

Progress on CodeGen

Code Generation and Transpilation is not an easy task, and our team is working diligently to solve this problem.  In the CoreRT repository on GitHub you can watch the progress the development team is making on the native compilation tools.  In particular, we had an interesting bit of progress over the summer as we completed some tasks that allow our C++ CodeGen to interpret .NET interface usage and properly dispatch it to C++.  We can now support transpiling interface usage in this baseline scenario:

You can find the source code commit that supports dispatching this type of interface structure on GitHub at: https://github.com/dotnet/corert/commit/c6a2b271f5cc6d5b4c271e1dd9ecb364b5a2743c   What makes this interesting is the inspection of the .NET dependency graph and object hierarchy to determine which method calls to actually dispatch from C++.

Interface Challenges Addressed

This seems like a simple step that we have taken, but there are several challenges that we faced when examining the code.

  • C++ does not have a 1-to-1 feature mapping to .NET. We need to interpret and determine the appropriate way to map .NET features into C++ calls appropriately.
  • Multiple .NET classes may implement the same interface. The transpiler must be smart enough to know which concrete class to instantiate and pass around as an interface.
    • It accomplishes this by building supporting data-structures (dispatch calls and a dispatch map) in a prescribed format and passes them to the runtime to figure out which method slot to dispatch on given that a particular interface method will likely live in a different slot on the various implementing concrete classes.  In this way, it works the same as the RyuJIT case: we just lay out the structures carefully in the generated C++ so that it is compatible with what the runtime expects.
  • The concrete class may implement virtual methods that belong to another interface or superclass. These interactions need to be preserved in C++

Summary

We believe that investing in C++ code generation is going to allow the .NET ecosystem to grow and support platforms that have not been invented yet.  With this approach, we no longer need to write JIT compilers for every environment, and can rely on the standardized and robust C++ compilers that are commonly available.  To learn more about CoreRT and the CodeGen processes being constructed, follow the CodeGen label on the CoreRT issues repository.  We’ll have more about CoreRT in the future as this framework progresses.


Comments (19)

  1. Sinix says:

    @Jeffrey T. Fritz

    Just a sidenote question: the current implementation of .net native includes a set of very promising optimizations such as cross-assembly AOT, aggressive inlining, vectorization, dead code elimination, and anything else that VC++ backend provides.

    Is the same true for CoreRT AOT or is it more like portable ngen?

    1. Mei-Chin Tsai [MSFT] says:

      Hi Sinix,

      This is a great question. We design CoreRT compiler pipeline to be able to host different code generation technology. RyuJit and IL2CPP are two mentioned in this article. Other code gen technology is not ruled out. The ultimate goal of having AOT compiler is to perform all the optimization that you mentioned and more.

      Thanks.

      1. Sinix says:

        @ Mei-Chin Tsai

        Ok, thanks for the clarification!

  2. GregD says:

    How did you make the choice to transpile into c++ versus (e.g.) C?

    1. Jan Kotas says:

      Transpiling into C++ allows for better debugging experience - what you see when you debug the traspilled C# code in C/C++ debugger. It would be a fairly simple change to transpile into C instead. The transpiled code does not have strong dependency on any advanced C++ features.

      The amount of code specific to CppCodeGen in CoreRT project is pretty small (a few thousands lines of C#). We are using the CppCodeGen backend to validate that the overall architecture is flexible enough to allow plugging in different codegen backends.

      CppCodeGen in CoreRT is quite similar to Unity IL2CPP that you can read more about at https://blogs.unity3d.com/2015/05/06/an-introduction-to-ilcpp-internals/. The difference between CoreRT CppCodeGen and Unity IL2CPP is in the amount of shared code between different flavors of the runtime: CoreRT CppCodeGen is designed to share with other flavors of the runtime; Unity IL2CPP has more unique parts.

      BTW: Here is a link to up-for-grabs issues if you would like to learn more about CoreRT CppCodeGen by getting your hands dirty: https://github.com/dotnet/corert/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20label%3ACodeGen-Cpp%20label%3AUpForGrabs%20

  3. YourMotherInLaw says:

    Why not link it directly to - I don't know - let's say assembler? 😉

    1. YourMotherInLaw says:

      By linking to assembler I mean C# -> IL -> assembler -> machine code. IL is already an assembler and you certainly can do C# -> ASM in 1:1.

      1. Jimmy R says:

        Isn't transpiling into assembler called compiling?

        1. Marco Caspers says:

          Assembly is still a text based human readable programming language (2nd generation language) that the computer doesn't understand anything about so i would still call that transpiling. Only when moving from programming language generation n down to 1st gen aka machine code aka binary code, i would call it compiling because it's only the binary code that the computer can do anything with. (Provided it's for the correct CPU of course)..

  4. fanoI says:

    Well IL should remain as one should want to do VB.NET to Assembler, F# to Assembler and so on... IL is the common "currency" of all the .NET languages.

    Direct IL to ASM translation is what we are doing in Cosmos OS with IL2CPU...

  5. onurg says:

    Please but please, do not ignore F# suuport this time as in the case of .NET Native

  6. Olivier says:

    Two questions:
    * I don't get exactly how mapping interfaces to C++ would ne particularité difficultés: don't they become pure abstract classes?
    * Can we expect generation of native exexutables with ryu or some DotNet et for regular desktop apps (i believe it only works for .net core and uwp for now)?

    1. Olivier says:

      Sorry for the typos. French autocomplete keyboard...

      1. Jan Kotas says:

        C# interfaces behave differently from C++ interfaces. For example, C# interfaces support contra and covariance that does not map well to C++ interfaces.

        CoreRT is alternative .NET Core runtime. It aims for roughly same apps as the mainstream .NET Core. It will support .NET Standard 2.0 that you can read about here: https://blogs.msdn.microsoft.com/dotnet/2016/09/26/introducing-net-standard/ .

  7. Kev says:

    Do you have plans to support f#?

    1. Jan Kotas says:

      Yes, we are keeping F# in mind. You can track progress towards enabling it in https://github.com/dotnet/corert/issues/2057

  8. Saurabh Gupta says:

    How is it different then what Xamarin already does?

  9. Serge says:

    Is reflection supported in il2cpp?

Skip to main content