To play games with unity, you need to have a deep understanding of il2cpp

Time:2019-11-11

This time, we translated an article on the official blog of unity. The original title is an integration to il2cpp interfaces. The author is Joshua Peterson who is engaged in the development of unity software. The point of this article is that it is told from the perspective of internal developers of il2cpp, so it is very valuable for developers.

AN INTRODUCTION TO IL2CPP INTERNALS

By Joshua Peterson
Translation: Bowie

About a year ago, we wrote a blog to discuss what scripts in unity will look like in the future. In that blog, we mentioned the new il2cpp backend and promised that it would bring unity more efficient and more suitable virtual machines for various platforms. In January 2015, we officially released the first platform using il2cpp: IOS 64 bit. With the release of unity 5, we have another platform for using il2cpp: webgl. Thanks for a lot of valuable feedback from users in our community, we can update il2cpp and release the patch version according to these feedback in the following time, so as to continuously improve the compiler and runtime library of il2cpp.

We have no intention to stop improving il2cpp, but at this point in time, we think we can go back and take some time to tell you some internal working mechanism of il2cpp. In the next few months, we plan to discuss the following topics (or other topics not listed) for an in-depth il2cpp series. At present, topics to be discussed include:

1. Basic – tool chain and command line parameters (this blog)
2. Il2cpp generation code introduction
3. Il2cpp code generation debugging tips
4. Method call Introduction (general method call, virtual method call, etc.)

  1. Implementation of common code sharing
    6. Encapsulation of P / invoke (platform invocation service) for types and methods
    7. Garbage collector integration
    8. Testing frameworks and their use

In order to make this series of discussions possible, we will cover some implementation details of il2cpp that will definitely be changed in the future. But it doesn’t matter. Through these discussions, we hope to provide you with some useful and interesting information.

What is il2cpp?

Technically speaking, il2cpp includes two parts: a compiler for precompiling.

A runtime library supporting virtual machines

The AOT compiler generates intermediate language (IL) code from. Net output into C + + code. Runtime libraries provide services and abstraction layers such as garbage collection, platform independent threads, IO, and internal calls (c + + native code directly accesses the managed code structure).

AOT compiler

The actual execution file of il2cpp AOT compiler is il2cpp.exe. On the windows platform, you can find it in the editor \ data \ il2cpp directory of the unity installation path. For the OSX platform, it is located in the contents / frameworks / il2cpp / build directory of the unity installation path Il2cpp.exe is a managed code executable, which is completely written by C. In the process of developing il2cpp, we compile it with. Net and mono compiler at the same time.

Il2cpp accepts managed assemblies from unity itself or generated by the mono compiler and converts them into C + + code. The converted C + + code is finally compiled by the C + + compiler on the deployment target platform.

You can refer to the figure below to understand the role of il2cpp tool chain:

 

Il2cpp tool chain

Runtime library

Another part of il2cpp is the runtime library that supports virtual machines. We basically use C + + code to implement the entire runtime library (well, in fact, there are some platform related codes that use assemblies, as long as you know what I know, don’t tell others). We call the runtime library libli2cpp, which is connected to the final game executable as a static library. One of the main benefits of this is that it makes the whole il2cpp technology simple and portable.

You can see how the code is organized by viewing the libil2cpp header file published with unity (for Windows platform, the header file is in the directory editor \ data \ playbackengines \ weblsupport \ buildtools \ libraries \ libil2cpp \ include). OSX platform, the header file is in the contents / frameworks / il2cpp / libil2cpp directory). For example, the interface API between C + + code generated by il2cpp and libil2cpp exists in CodeGen / il2cpp CodeGen. H.

Another important part of the runtime is the garbage collector. In unity 5, we use the libgc garbage collector. It is a typical Boehm Demers Weiser garbage collector. Relatively conservative garbage collection strategy is used. However, our libil2cpp is designed to be easy to use with other garbage collectors. So we are also working on integrating Microsoft’s open source garbage collector (Microsoft GC). As for the garbage collector, we will discuss it in a subsequent article, which will not be discussed here.

How is il2cpp implemented?

Let’s start with a simple example. The version of unity used here is 5.0.1, which is in Windows environment and creates a new empty project. Then create a script file with monobehaviour and add it to the main camera as a component. The code is also very simple. Output Hello World:

  1.  
    using UnityEngine;
  2.  
     
  3.  
    public class HelloWorld : MonoBehaviour {
  4.  
    void Start () {
  5.  
    Debug.Log(“Hello, IL2CPP!”);
  6.  
    }
  7.  
    }

When I switch to webgl platform for project generation, we can use process explorer to observe the command line of il2cpp and get the following contents:
“C:\Program Files\Unity\Editor\Data\MonoBleedingEdge\bin\mono.exe” “C:\Program Files\Unity\Editor\Data\il2cpp/il2cpp.exe” –copy-level=None –enable-generic-sharing –enable-unity-event-support –output-format=Compact –extra-types.file=”C:\Program Files\Unity\Editor\Data\il2cpp\il2cpp_default_extra_types.txt” “C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\Managed\Assembly-CSharp.dll” “C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\Managed\UnityEngine.UI.dll” “C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\il2cppOutput”

Well, this is the old lady’s foot binding – smelly and long… So let’s split the command. Unity runs the executable file:

“C:\Program Files\Unity\Editor\Data\MonoBleedingEdge\bin\mono.exe”

The next parameter is the il2cpp.exe tool itself:
“C:\Program Files\Unity\Editor\Data\il2cpp/il2cpp.exe”

Please note that the remaining parameters are actually passed to il2cpp.exe instead of mono.exe. In the above example, five parameters are passed to il2cpp.exe:
–copy-level=None

Indicates that il2cpp.exe does not copy the generated C + + file
–enable-generic-sharing
Tell il2cpp to share common methods if possible. This reduces the code and reduces the size of the final binary

–enable-unity-event-support
Ensure that the code related to unity events that operates through reflection mechanism can be generated correctly.

–output-format=Compact
Use shorter names for types and methods in C + + code generation. This makes C + + code hard to read because the original name in IL is replaced by a shorter one. But the advantage is that the C + + compiler can run faster.

–extra-types.file=”C:\Program Files\Unity\Editor\Data\il2cpp\il2cpp_default_extra_types.txt”

Use the default (and empty) extra type file. Il2cpp.exe treats the basic types or array types that appear in this file as if they were generated at run time rather than initially in the IL code.

It should be noted that these parameters may change in later versions of unity. We have not stabilized to fix the command line parameters of il2cpp.exe.

Finally, we have a list of two files and a directory on this long command line:
“C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\Managed\Assembly-CSharp.dll”

“C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\Managed\UnityEngine.UI.dll”

“C:\Users\Josh Peterson\Documents\IL2CPP Blog Example\Temp\StagingArea\Data\il2cppOutput”

The il2cpp.exe tool can receive a list of IL assemblies. In the example above, the assembly contains the simple script assembly in the project: assembly-csharp.dll, and the GUI assembly: unityengine.ui.dll. You may notice what’s missing: where is unityengine.dll? Mscorlib.dll at the bottom of the system is also missing. In fact, il2cpp.exe automatically references these assemblies internally. You can put these in the list, of course, but they are not necessary. You just need to mention the root assemblies (those that are not referenced by any other assemblies), and the remaining il2cpp.exe will be added automatically according to the reference relationship.

The last piece of the shroud is a directory where il2cpp.exe will generate the final C + + code. If you’re still curious, look at the files generated in this directory. These papers are the subject of our next discussion. Before you look at the code, consider checking the “development player” option in the webgl build settings. Doing so removes the – output format = compact command-line parameter to make the names of types and methods in C + + code more readable.

Try to make some changes in webgl or IOS build settings. In this way, you will find that the parameters passed to il2cpp.exe will change accordingly. For example, setting “enable exceptions” to “full” will add the three parameters – emit null checks, – enable stacktrace, and – enable array bounds check to the il2cpp.exe command line.

What il2cpp didn’t do

I would like to point out that il2cpp has always been a challenge that we didn’t accept, and we are glad that we ignored it. We didn’t try to rewrite the entire C standard library. When you use the IL2CPP backend to build Unity projects, all the C# Standard Libraries in mscorlib.dll, System.dll and so on are exactly the same as those used in Mono compilation.

We can rely on the robust and proven C standard library, so when dealing with the bugs related to il2cpp, we can be sure that the problem lies in AOT compiler or runtime library rather than other places.

How do we develop, test and release il2cpp

Since we first introduced il2cpp in 4.6.1 P5 in January, we have released six unity versions and seven patches in a row (unity version number spans 4.6 and 5.0). We fixed more than 100 bugs in these releases.

In order to ensure the continuous improvement can be implemented, we only keep a copy of the latest development code on the trunk branch. Before releasing each version, we will hang the changes of il2cpp under a specific score, and then test to ensure that all bugs have been corrected correctly. Our QA and maintenance teams have made an amazing effort to ensure a quick iteration of the release. It feels like a standard development process for version management

The user community that provides high-quality bugs has proved invaluable. We are very grateful for the feedback from users to help us improve il2cpp, and hope that the more such feedback, the better.

Our il2cpp R & D team has a strong sense of “test first”. We often use the “test driven design” method. Without a comprehensive enough test, we can hardly merge the code. This strategy is very good for il2cpp project. Most of the bugs we face now are not caused by unexpected behaviors, but by unexpected special situations. (for example, using 64 bit pointer in a 32-bit index array leads to C + + compiler failure) we can quickly and confidently correct this type of bug.

With the help of the community, we make great efforts to make il2cpp fast and stable. By the way, if you’re interested in what I’ve just said, we’re hiring (well, I’m just saying that)

one good show after another

We have a lot to say about il2cpp. Next time, we will go into the details of il2cpp.exe code generation. See what the code generated by il2cpp.exe looks like for the C + + compiler.

By indieace
Link: https://www.jianshu.com/p/dd430c991d0b
Source: Jianshu
The copyright of the brief book belongs to the author. For any reprint, please contact the author for authorization and indicate the source.