I’m working on a kotlin idomatic library around the llvm C api which is coming along really well actually. I’m also making a full fledged custom language and compiler to test it out.
Links:
kotlinx-llvm
toylang
I’m working on implementing my latest features which is function calls, and some instructions like the ptrtoint instruction and the load instruction. Besides that, there is a checklist in the repo for the things that have been done.
The brute test in LLVM.kt produces this:
; ModuleID = 'test'
source_filename = "test"
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc19.23.28106"
@testVar = global i32 10
declare i32 @printf(i8*, ...)
define i32 @add(i32, i32 %y) {
entry:
%add_res = add i32 %0, %y
ret i32 %add_res
}
define i32 @main() {
test_block_1:
%message = alloca [3 x i8]
store [3 x i8] c"%d\00", [3 x i8]* %message
%messageGEP_ref = getelementptr [3 x i8], [3 x i8]* %message, i32 0
%addCallRes = alloca i32
%addFuncCall = call i32 @add(i32 10, i32 10)
store i32 %addFuncCall, i32* %addCallRes
%gepCast = bitcast [3 x i8]* %messageGEP_ref to i8*
%0 = load i32, i32* %addCallRes
%call = call i32 (i8*, ...) @printf(i8* %gepCast, i32 %0)
ret i32 0
}
And when built into an executable executes
//Compile the llvm bitcode
C:\projects\kotlinx-llvm (master -> origin)
λ llc test.bc
//Build the build binary object into an executable
C:\projects\kotlinx-llvm (master -> origin)
λ clang -o test.exe test.
test.bc test.c test.exe test.ll test.o test.s
C:\projects\kotlinx-llvm (master -> origin)
λ clang -o test.exe test.s
//Run the generated executable
C:\projects\kotlinx-llvm (master -> origin)
λ test.exe
20
Right now, the default llvm passes inline a lot of the instructions so there are places where you would see a temporary variable made that references or loads the value of some pointer that actually get inlined, like in the @add(i32 10, i32 10)
, we are actually trying to load the value of @testVar
but because @testVar
is just a primitive integer literal, it gets inlined. I’ll be adding custom llvm passes soon enough.
The custom language should be usable minimally as a language soon once I get function calls to work properly. I’ve almost got it done, but I had recently discovered that my ANTLR parser grammar was inefficient and was crashing the intellij plugin but I had fixed it and had made a significant improvement to the performance as told by the profiler (I’ll post about it tonight after I’m done with work). When I get the toylang to compile with function calls working, I’ll make a demo video and post here!
UPDATE!!
Just now I have finally implemented the new kotlinx-llvm features into the toylang compiler and was finally able to produce a binary of a more complex program.
let test = "Hello\n";
fn add(x: Int, y: Int): Int{
return x + y;
};
fn main(argc: Int){
println(test);
println("%d\n", add(5, 6));
};
C:\projects\kotlinx-llvm\toylang\run (master -> origin)
λ llc -filetype=obj -o test.o test.bc
C:\projects\kotlinx-llvm\toylang\run (master -> origin)
λ clang -o test.exe test.o
C:\projects\kotlinx-llvm\toylang\run (master -> origin)
λ test.exe
Hello\n11\n
Just a quick checklist of things:
- Refactor toylang compiler so that the scope information is generated by the parsing visitor rather than a separate component
- Add compilation support for string interpolation rather than just lexical/parsing support
- Create dedicated passes for several complex mechanisms
- Type Checking
- String interpolation
- Type Information (Classes, structs, properties, etc)
- “Module Catenation” aka The unionizing of modules into one super module
- Complex error handling
- Add in structs
- I want to make functions first classes and allow pure functions as a feature
- Improve local variables
A note to make about this toylang is that this is not Beagle, this is simply an educational language that is designed with simplicity and to test kotlinx-llvm properly and show people how kotlinx-llvm can be used and more importantly show how a compiler can be made.