|
| 1 | +# Ahead of Time Compilation |
| 2 | + |
| 3 | +## Introduction |
| 4 | + |
| 5 | +The mono Ahead of Time (AOT) compiler enables the compilation of the IL code in a .NET assembly to |
| 6 | +a native object file. This file is called an AOT image. This AOT image can be used by the runtime to avoid |
| 7 | +having to JIT the IL code. |
| 8 | + |
| 9 | +## Usage |
| 10 | + |
| 11 | +The AOT compiler is integrated into the mono runtime executable, and can be run using the `--aot` command |
| 12 | +line argument, i.e. |
| 13 | +`<mono-executable> --aot HelloWorld.dll` |
| 14 | + |
| 15 | +## Source code structure |
| 16 | + |
| 17 | +- `aot-compiler.c`: The AOT compiler |
| 18 | +- `aot-runtime.c`: Code used at runtime to load AOT images |
| 19 | +- `image-writer.c`: Support code for emitting textual assembly |
| 20 | +- `dwarfwriter.c`: Support code for emitting DWARF debug info |
| 21 | + |
| 22 | +## Configurations |
| 23 | + |
| 24 | +### Desktop AOT |
| 25 | + |
| 26 | +In this mode, the AOT compiler creates a platform shared object file (.so/.dylib), i.e. `HelloWorld.dll.so`. During execution, when |
| 27 | +an assembly is loaded, the runtime loads the corresponding shared object and uses it to avoid having to AOT the methods in the |
| 28 | +assembly. |
| 29 | + |
| 30 | +Emission of the native code is done by first emitting an assembly (.s) file, then compiling and linking it with the system tools |
| 31 | +(`as`/`ld`, or `clang`). |
| 32 | + |
| 33 | +### Static AOT |
| 34 | + |
| 35 | +In this mode, the AOT compiler creates a platform object file (.o). This file needs to be linked into the application and registered |
| 36 | +with the runtime. |
| 37 | + |
| 38 | +Static compilation is enabled by using the `static` aot option, i.e. `--aot=static,...`. The resulting object file contains a linking |
| 39 | +symbol named `mono_aot_module_<assembly name>_info`. This symbol needs to be passed to the a runtime function before the |
| 40 | +runtime is initialized, i.e.: |
| 41 | +`mono_aot_register_module (mono_aot_module_HelloWorld_info);` |
| 42 | + |
| 43 | +### Full AOT |
| 44 | + |
| 45 | +In this mode, which can be combined with the other modes, the compiler generates additional code which enables the runtime to |
| 46 | +function without any code being generated at runtime. This includes 2 types of code: |
| 47 | +- code for 'extra' methods, i.e. generic instances, runtime generated wrappers methods, etc. |
| 48 | +- trampolines |
| 49 | + |
| 50 | +This is enabled by using `full` aot option, i.e. `--aot=full,...`. At runtime, all assemblies need to have a full-aot-ed AOT image |
| 51 | +present in order for the app to work. This is used on platforms which don't allow runtime code generation like IOS. |
| 52 | + |
| 53 | +### LLVM support |
| 54 | + |
| 55 | +LLVM support can be enabled using the `llvm` aot option, i.e. `--aot=llvm`. In this mode, instead of generating native code, |
| 56 | +the AOT compiler generates an LLVM bitcode (.bc), file, then compiles it to native code using the `opt`/`llc` LLVM tools. The |
| 57 | +various AOT data structures are also emitted into the .bc file instead of as assembly. |
| 58 | +Since the LLVM backend currently doesn't support all .net methods, a smaller assembly file is still emitted, and linked together |
| 59 | +with the `opt`/`llc` compiled object file into the final shared object file. |
| 60 | + |
| 61 | +## Versioning |
| 62 | + |
| 63 | +The generated AOT images have a dependency on the exact version input assembly used to generate them and the versions of all the |
| 64 | +referenced assemblies. This means the GUIDs of the assemblies have to match. If there is a mismatch, the AOT image will fail to load. |
| 65 | + |
| 66 | +## File structure |
| 67 | + |
| 68 | +The AOT images exports one symbol named `mono_aot_module_<assembly name>_info` which points to a `MonoAotFileInfo` structure, |
| 69 | +which contains pointers to the tables/structures. The AOT image contains: |
| 70 | +- the native code |
| 71 | +- data structures required to load the code |
| 72 | +- cached data intended to speed up runtime operation |
| 73 | + |
| 74 | +The AOT image contains serialized versions of many .NET objects like methods/types etc. This uses ad-hoc binary encodings. |
| 75 | + |
| 76 | +## Runtime support |
| 77 | + |
| 78 | +The `aot-runtime.c` file contains the runtime support for loading AOT images. |
| 79 | + |
| 80 | +### Loading AOT images |
| 81 | + |
| 82 | +When an assembly is loaded, the corresponding AOT images is either loaded using the system dynamic linker (`dlopen`), or |
| 83 | +found among the statically linked AOT images. |
| 84 | + |
| 85 | +### Loading methods |
| 86 | + |
| 87 | +Every method in the AOT image is assigned an index. The AOT methods corresponding to 'normal' .NET methods are assigned |
| 88 | +an index corresponding to their metadata token index, while the 'extra' methods are assigned subsequent indexes. There is |
| 89 | +a hash table inside the AOT image mapping extra methods to their AOT indexes. Loading a method consists of |
| 90 | +- finding its method index |
| 91 | +- finding the method code/data corresponding to the method index |
| 92 | + |
| 93 | +The mapping from method index to the code is done in an architecture specific way, designed to minimize the amount of |
| 94 | +runtime relocations in the AOT image. In some cases, this involves generating an extra table with assembly call instructions to |
| 95 | +all the methods, then disassembling this table at runtime. |
| 96 | + |
| 97 | + |
| 98 | + |
| 99 | +### Runtime constants |
| 100 | + |
| 101 | +The generated code needs to access data which is only available at runtime. For example, for an `ldstr "Hello"` instruction, the |
| 102 | +`"Hello"` string is a runtime constant. |
| 103 | + |
| 104 | +These constants are stored in a global table called the GOT which is modelled after the Global Offset Table in ELF images. The GOT |
| 105 | +table contains pointers to runtime objects. The AOT image contains descriptions of these runtime objects so the AOT runtime can |
| 106 | +compute them. The entries in the GOT are initialized either when the AOT image is loaded (for frequently used entries), or before |
| 107 | +the method which uses them is first executed. |
| 108 | + |
| 109 | +### Initializing methods |
| 110 | + |
| 111 | +Before an AOTed method can be executed, it might need some initialization. This involves: |
| 112 | +- executing its class cctor |
| 113 | +- initializing the GOT slots used by the method |
| 114 | + |
| 115 | +For methods compiled by the mono JIT, initialization is done when the method is loaded. This means that its not possible to |
| 116 | +have direct calls between methods. Instead, calls between methods go through small pieces of generated code called PLT |
| 117 | +(Program Linkage Table) entries, which transfer control to the runtime which loads the called method before executing it. |
| 118 | +For methods compiled by LLVM, the method entry contains a call to the runtime which initializes the method. |
| 119 | + |
| 120 | +## Trampolines |
| 121 | + |
| 122 | +In full-aot mode, the AOT compiler needs to emit all the trampolines which will be used at runtime. This is done in |
| 123 | +the following way: |
| 124 | +- For most trampolines, the AOT compiler calls the normal trampoline creation function with the `aot` argument set |
| 125 | +to TRUE, then saves the returned native code into the AOT image, along with some relocation information like the |
| 126 | +GOT slots used by the trampolines. |
| 127 | +- For some small trampolines, the AOT compiler directly emits platform specific assembly. |
| 128 | + |
| 129 | +The runtime might require an unbounded number of certain trampolines, but the AOT image can only contain a fixed |
| 130 | +number of them. To solve this problem, on some platforms (IOS), its possible to have infinite trampolines. This is |
| 131 | +implemented by emitting a different version of these trampolines which reference their corresponding data using |
| 132 | +relative addressing. At runtime, a page of these trampolines is mapped using `mmap` next to a writable page |
| 133 | +which contains their corresponding data. The same page of trampolines is mapped multiple times at multiple |
| 134 | +addresses. |
| 135 | + |
| 136 | +## Cross compilation |
| 137 | + |
| 138 | +Its possible to use the AOT compiler to target a platform different than the host. This requires a separate cross compiler |
| 139 | +build of the runtime. |
| 140 | +The generated code depends on offsets inside runtime structures like `MonoClass`/`MonoVTable` etc. which could |
| 141 | +differ between the host and the target. This is handled by having a tool called the offsets-tool, which is a python |
| 142 | +script which uses the clang python interface to compute and emit a C header file containing these offsets. The header |
| 143 | +file is passed as a cmake argument during the runtime build. Inside the runtime code, the `MONO_STRUCT_OFFSET` |
| 144 | +C macro reads the data from the offsets file to produce the offset corresponding to the target platform. |
0 commit comments