百科.dev
全部条目AI 编程趋势榜开源项目技术资讯提交条目
登录
< 返回工具列表
M

mir

> 编程语言
开源

基于 MIR (中级内部表示) 的轻量级 JIT 编译器,以及基于 MIR 的 C11 JIT 编译器和解释器

2.6K stars0 点赞0 次浏览
访问官网GitHub

工具介绍

基于 MIR (中级内部表示) 的轻量级 JIT 编译器,以及基于 MIR 的 C11 JIT 编译器和解释器

# MIR Project * MIR means **M**edium **I**nternal **R**epresentation * MIR project goal is to provide a basis to implement fast and lightweight JITs * Plans to try MIR light-weight JIT first for CRuby or/and MRuby implementation * Motivations for the project can be found in [this blog post](https://developers.redhat.com/blog/2020/01/20/mir-a-lightweight-jit-compiler-project) * C2MIR compiler description can be found in [this blog post](https://developers.redhat.com/blog/2021/04/27/the-mir-c-interpreter-and-just-in-time-jit-compiler) * Future of code specialization in MIR for dynamic language JITs can be found in [this blog post](https://developers.redhat.com/articles/2022/02/16/code-specialization-mir-lightweight-jit-compiler) ## Disclaimer * **There is absolutely no warranty that the code will work for any tests except ones given here and on platforms other than x86_64 Linux/OSX, aarch64 Linux/OSX(Apple M1), and ppc64le/s390x/riscv64 Linux** ## MIR * MIR is strongly typed IR * MIR can represent machine 32-bit and 64-bit insns of different architectures * [MIR.md](https://github.com/vnmakarov/mir/blob/master/MIR.md) contains detail description of MIR and its API. Here is a brief MIR description: * MIR consists of **modules** * Each module can contain **functions** and some declarations and data * Each function has **signature** (parameters and return types), **local variables** (including function arguments) and **instructions** * Each local variable has **type** which can be only 64-bit integer, float, double, or long double and can be bound to a particular target machine register * Each instruction has **opcode** and **operands** * Operand can be a local variable (or a function argument), **immediate**, **memory**, **label**, or **reference** * Immediate operand can be 64-bit integer, float, double, or long double value * Memory operand has a **type**, **displacement**, **base** and **index** integer local variable, and integer constant as a **scale** for the index * Memory type can be 8-, 16-, 32- and 64-bit signed or unsigned integer type, float type, double, or long double type * When integer memory value is used it is expanded with sign or zero promoting to 64-bit integer value first * Label operand has name and used for control flow instructions * Reference operand is used to refer to functions and declarations in the current module, in other MIR modules, or for C external functions or declarations * opcode describes what the instruction does * There are **conversion instructions** for conversion between different 32- and 64-bit signed and unsigned values, float, double, and long double values * There are **arithmetic instructions** (addition, subtraction, multiplication, division, modulo) working on 32- and 64-bit signed and unsigned values, float, double, and long double values * There are **logical instructions** (and, or, xor, different shifts) working on 32- and 64-bit signed and unsigned values * There are **comparison instructions** working on 32- and 64-bit signed and unsigned values, float, double, and long double values * There are **local variable address instructions** to get address of local variable * There are **branch insns** (unconditional jump, and jump on zero or non-zero value) which take a label as one their operand * There are **combined comparison and branch instructions** taking a label as one operand and two 32- and 64-bit signed and unsigned values, float, double, and long double values * There is **switch** instruction to jump to a label from labels given as operands depending on index given as the first operand * There is **label address instruction** to get a label address and **unconditional indirect jump instruction** whose operand contains previously taken label address * There are **function and procedural call instructions** * There are **return instructions** optionally returning 32- and 64-bit integer values, float, double, and long double values * There are **specialized light-weight call and return instructions** can be used for fast switching from threaded interpreter to JITted code and vice verse * There are **property** instructions to generated specialized machine code when lazy basic block versioning is used ## MIR Example * You can create MIR through **API** consisting of functions for creation of modules, functions, instructions, operands etc * You can also create MIR from MIR **binary** or **text** file * The best way to get a feel about MIR is to use textual MIR representation * Example of Eratosthenes sieve on C ```c #define Size 819000 int sieve (int N) { int64_t i, k, prime, count, n; char flags[Size]; for (n = 0; n < N; n++) { count = 0; for (i = 0; i < Size; i++) flags[i] = 1; for (i = 0; i < Size; i++) if (flags[i]) { prime = i + i + 3; for (k = i + prime; k < Size; k += prime) flags[k] = 0; count++; } } return count; } void ex100 (void) { printf ("sieve (100) = %d\", sieve (100)); } ``` * Example of MIR textual file for the same function: ``` … ``` * `func` describes signature of the function (taking 32-bit signed integer argument and returning 32-bit signed integer value) and function argument `N` which will be local variable of 64-bit signed integer type * Function results are described first by their types and have no names. Parameters always have names and go after the result description * Function may have more than one result but possible number and combination of result types are currently machine defined * You can write several instructions on one line if you separate them by `;` * The instruction result, if any, is always the first operand * We use 64-bit instructions in calculations * We could use 32-bit instructions in calculations which would have sense if we use 32-bit CPU * When we use 32-bit instructions we take only 32-bit significant part of 64-bit operand and high 32-bit part of the result is machine defined (so if you write a portable MIR code consider the high 32-bit part value is undefined) * `string` describes data in form of C string * C string can be used directly as an insn operand. In this case the data will be added to the module and the data address will be used as an operand * `export` describes the module functions or data which are visible outside the current module * `import` describes the module functions or data which should be defined in other MIR modules * `proto` describes function prototypes. Its syntax is the same as `func` syntax * `call` are MIR instruction to call functions ## Running MIR code * After creating MIR modules (through MIR API or reading MIR binary or textual files), you should load the modules * Loading modules makes visible exported module functions and data * You can load external C function with `MIR_load_external` * After loading modules, you should link the loaded modules * Linking modules resolves imported module references, initializes data, and set up call interfaces * After linking, you can interpret functions from the modules or call machine code for the functions generated with MIR JIT compiler (generator). What way the function can be executed is usually defined by set up interface. How the generated code is produced (lazily on the first call or ahead of time) can be also dependent on the interface * Running code from the above example could look like the following (here `m1` and `m2` are modules `m_sieve` and `m_e100`, `func` is function `ex100`, `sieve` is function `sieve`): ``` … ``` ### Running binary MIR files on Linux through `binfmt_misc` The `mir-bin-run` binary is prepared to be used from `binfmt_misc` with the following line (example): ```bash line=:mir:M::MIR::/usr/local/bin/mir-bin-run:P echo $line > /proc/sys/fs/binfmt_misc/register ``` > Do adapt the mir-bin-run binary path to your system, that is the default one And run with ```bash c2m your-file.c -o your-file chmod +x your-file ./your-file your args ``` The executable is "configurable" with environment variables: * `MIR_TYPE` sets the interface for code execution: `interp` (for interpretation), `jit` (for generation) and `lazy` (for lazy generation, default); * `MIR_LIBS` (colon separated list) defines a list of extra libraries to load; * `MIR_LIB_DIRS` or `LD_LIBRARY_PATH` (colon separated list) defines an extra list of directories to search the libraries on. > Due to the tied nature of `mir-bin-run` with `binfmt_misc`, it may be a bit weird > to call `mir-bin-run` directly. > The `P` flag on the binfmt_misc passes an extra argument with the full path > to the MIR binary. ## The current state of MIR project * You can use C **setjmp/longjmp** functions to implement **longjump** in MIR * Binary MIR code is usually upto **10 times more compact** and upto **10 times faster to read** than analogous MIR textual code * MIR interpreter is about 6-10 times slower than code generated by MIR JIT compiler * LLVM IR to MIR translator has not been finished and probably will be never fully implemented as LLVM IR is much richer than MIR but translation of LLVM IR generated from standard C/C++ to MIR is a doable task ## The possible future state of MIR project * WASM to MIR translation should be pretty straightforward * Only small WASM runtime for WASM floating point round insns needed to be provided for MIR * Porting GCC to MIR is possible too. An experienced GCC developer can implement this for 6 to 12 months * On my estimation porting MIR JIT compiler to mips64 or sparc64 will take 1-2 months of work for each target * Performance minded porting MIR JIT compiler to 32-bit targets will need an implementation of additional small analysis pass to get info what 64-bit variables are used only in 32-bit instructions ## MIR JIT compiler * Very short optimization pipeline for speed and light-weight * Only the **most valuable** optimization usage: * **function inlining** * **global common sub-expression elimination** * **variable renaming** * **register pressure sensitive loop invariant code motion** * **conditional constant propagation** * **dead code elimination** * **code selection** * fast **register allocator** with * aggressive coalescing registers and stack slots for copy elimination * live range splitting * Different optimization levels to tune compilation speed vs generated code performance * **SSA** form of MIR is used before register allocation * We use a form of Braun's algorithm to build SSA (M. Braun et al. "Simple and Efficient Construction of Static Single Assignment Form") * Simplicity of optimizations implementation over extreme generated code performance * More details about **full JIT compiler pipeline**: * **Simplify**: lowering MIR * **Inline**: inlining MIR calls * **Build CFG**: building Control Flow Graph (basic blocks and CFG edges) * **Build SSA**: Building Single Static Assignment

GitHub Issues· 0 开放

在 GitHub 查看全部

暂无开放 Issues,或尚未同步最近议题。

核心特点

  • •MIR means Medium Internal Representation
  • •MIR project goal is to provide a basis to implement fast and lightweight JITs
  • •Plans to try MIR light-weight JIT first for CRuby or/and MRuby implementation
  • •Motivations for the project can be found in this blog post
  • •C2MIR compiler description can be found in this blog post
  • •Future of code specialization in MIR for dynamic language JITs can be found in this blog post
  • •There is absolutely no warranty that the code will work for any tests except ones given here and on platforms
  • •MIR is strongly typed IR
  • •MIR can represent machine 32-bit and 64-bit insns of different architectures
  • •MIR.md contains detail description of MIR and its API.

> 标签

Caarch64appleccompiler

暂无评论,来聊聊你的看法吧

> 工具信息

发布日期2026年8月1日
最后更新2026年9月17日
分类编程语言
定价开源

> 相关工具

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言