Use LLVM by JavaScript/TypeScript

Background

As a developer, have you ever wanted to implement your own compiler in the past?

Fortunately, in 2021, implementing a compiler is no longer a difficult task because of LLVM Website.

LLVM is a set of compiler infrastructure projects, a compiler framework. Both Rust and Swift use LLVM as their compiler backend.

Everyone can implement their own compiler based on LLVM.

But... LLVM is written in C++, which means that if you want to use LLVM, you must also use C++. Not everyone likes C++.

So I developed a library called llvm-bindings to allow you to use LLVM by JavaScript. Of course, there is no problem using LLVM through TypeScript.

Install

First, you need to install CMake and LLVM before llvm-bindings.

Install CMake and LLVM on macOS

brew install cmake llvm

Install CMake and LLVM on Ubuntu

sudo apt-get install cmake
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"

After making sure that CMake and LLVM are installed correctly on your system, you can install llvm-bindings through npm.

npm install llvm-bindings

Usage

We will write all our code in a file called demo.js

First, import llvm-bindings

const llvm = require('llvm-bindings');

Then, new a Context, a Module and a IR Builder

const context = new llvm.LLVMContext();
const mod = new llvm.Module('demo', context);
const builder = new llvm.IRBuilder(context);

We call this module mod because Node.js does not allow variables called module to be declared in the top level.

Then, build objects related to function signatures

const returnType = builder.getInt32Ty();
const paramTypes = [builder.getInt32Ty(), builder.getInt32Ty()];
const functionType = llvm.FunctionType.get(returnType, paramTypes, false);
const func = llvm.Function.Create(functionType, llvm.Function.LinkageTypes.ExternalLinkage, 'add', mod);

Then, build the basic blocks and instructions within the function

const entryBB = llvm.BasicBlock.Create(context, 'entry', func);
builder.SetInsertPoint(entryBB);
const a = func.getArg(0);
const b = func.getArg(1);
const result = builder.CreateAdd(a, b);
builder.CreateRet(result);

Finally, verify the function and the module, and print the LLVM IR of the entire module in text form

if (llvm.verifyFunction(func)) {
    console.error('Verifying function failed');
    return;
}
if (llvm.verifyModule(mod)) {
    console.error('Verifying module failed');
    return;
}
mod.print();

Now you have implemented a basic example of using LLVM through JavaScript, by executing node demo.js, you can see:

Conclusion

llvm-bindings provides developers with the ability to use LLVM through JavaScript/TypeScript. Friends who need to use llvm can try llvm-bindings.

llvm-bindings is currently under development on demand. Maybe the LLVM API you need has not been added yet. You can submit an issue to request a new API. I will add it within one day of receiving the issue.

Full Code Listing

const llvm = require('llvm-bindings');

const context = new llvm.LLVMContext();
const mod = new llvm.Module('demo', context);
const builder = new llvm.IRBuilder(context);

const returnType = builder.getInt32Ty();
const paramTypes = [builder.getInt32Ty(), builder.getInt32Ty()];
const functionType = llvm.FunctionType.get(returnType, paramTypes, false);
const func = llvm.Function.Create(functionType, llvm.Function.LinkageTypes.ExternalLinkage, 'add', mod);

const entryBB = llvm.BasicBlock.Create(context, 'entry', func);
builder.SetInsertPoint(entryBB);
const a = func.getArg(0);
const b = func.getArg(1);
const result = builder.CreateAdd(a, b);
builder.CreateRet(result);

if (llvm.verifyFunction(func)) {
    console.error('verifying the function failed');
    return;
}
if (llvm.verifyModule(mod)) {
    console.error('verifying the module failed');
    return;
}
mod.print();

main();

Note

Currently llvm-bindings only supports macOS and Ubuntu systems, and Windows is not yet supported.

References

21