Use LLVM by JavaScript/TypeScript
Tags: typescript, javascript, llvm, compiler
Published: 2021-12-18 15:48:31

## 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](https://llvm.org).

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](https://github.com/ApsarasX/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
```sh
brew install cmake llvm
```

Install CMake and LLVM on Ubuntu
```sh
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.
```sh
npm install llvm-bindings
```

## Usage
> We will write all our code in a file called `demo.js`

First, import `llvm-bindings`
```js
const llvm = require('llvm-bindings');
```

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

```js
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

```js
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

```js
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

```js
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:


![Print Result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ulru5ovnmr8lbdj2jxle.png)
## 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
```js
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
- https://llvm.org
- https://github.com/ApsarasX/llvm-bindings