Skip to content

Commit d90f8be

Browse files
AlexDenisovasl
authored andcommitted
Add more details about LLVM/MLIR
1 parent ff27724 commit d90f8be

File tree

4 files changed

+21
-5
lines changed

4 files changed

+21
-5
lines changed

content/posts/2024-12-03-minimalistic-ruby-compiler.md

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ We started the project with certain goals and hypotheses in mind, and while the
1010

1111
In the meantime, we decided to strip it down to a bare minimum and implement just enough features to validate the hypotheses.
1212

13-
The source code of our minimalistic Ruby compiler is here: https://github.com/dragonruby/lightstorm
13+
Just like the original compiler we use MLIR to bridge the gap between Ruby VM's bytecode and the codegen, but instead of targeting LLVM IR directly, we go through EmitC dialect and targeting C language, as it significantly simplifies OS/CPU support. We go into a bit more details later.
14+
15+
The source code of our minimalistic Ruby compiler is here: https://github.com/dragonruby/lightstorm.
1416

1517
The rest of the article covers why we decided to build it, how we approached the problem, and what we discovered along the way.
1618

@@ -142,12 +144,26 @@ This eliminates a lot of complexity, but it also means that we only support a su
142144

143145
This is obviously not ideal, but it serves important purpose - **our goal at this point is to validate the hypothesis**.
144146

145-
We convert VM's bytecode into MLIR representation, and then lower it to C.
146-
At that point, we can just use clang to compile/link the code together with the existing runtime and that's it.
147+
A classical compilation pipeline looks as follows:
148+
149+
![Classical compilation pipeline](/img/ruby-compiler/compilation-pipeline.png)
150+
151+
152+
To build a compiler one needs to implement the conversions from the raw source file all the way down to machine code and the language runtime library.
153+
Since we are targeting the existing runtime, we have the benefit of reusing the frontend (parsing + AST) and the runtime.
154+
155+
Still, we need to implement the conversion from AST to the machine code.
156+
And this is where the power of MLIR kicks in: we built a custom dialect ([Rite](https://github.com/DragonRuby/lightstorm/blob/3ed0077af589ba51b98954bba8869daf58e22b9e/include/lightstorm/dialect/rite.td)) which represents mruby VM's bytecode, and then using a number of builtin dialects (`cf`, `func`, `arith`, `emitc`) to convert our IR into C code.
157+
158+
At this point, we can just use clang to compile/link the code together with the existing runtime and that's it.
159+
160+
The final compilation pipeline looks as follows:
161+
162+
![Lightstorm compilation pipeline](/img/ruby-compiler/lightstorm-compilation-pipeline.png)
147163

148-
Here is an illustration of this approach (taken from the [EuroLLVM talk](https://www.youtube.com/watch?v=NfMX-dFMSr0)).
164+
With the benefit of MLIR we are able to build a funtional compiler in just a couple of thousands lines of code!
149165

150-
![How to compile a dynamic language](/img/ruby-compiler/how-to-compile-dynamic-language.png)
166+
Now let's look at how it performs.
151167

152168
## Some numbers
153169

650 KB
Loading
-666 KB
Binary file not shown.
679 KB
Loading

0 commit comments

Comments
 (0)