LLVM Sauce is a yummy Source to LLVM IR compiler. More specifically, it implements a frontend that takes in a Source program and outputs LLVM Intermediate Representation (IR). This IR can then be optimized, interpreted or compiled further with other tools.
A technical specification for the compiler can be found at here.
- A fully featured Source 1 language compiler
- A compiler that respects the Proper Tail Call (PTC) semantics of ECMAScript 2015 Language Specification
- A 64 bit Linux System
Notes:
- Currently build and tested on
Ubuntu 20.04.1
,Debian Buster
,NixOS 20.09
withLLVM 10.0.0
. - Windows and MacOS are currently unsupported due to CMake dependencies on
llvm-node
.
System Dependencies:
- LLVM Framework (technically any version should work)
- Nodejs
- Yarn
- G++
- CMake
Refer to this seperate guide on installing system dependencies using Nix.
- Clone repository to your local file system
- Enter the project directory
- Install node dependencies
- Build project
- Run tests to ensure that everything is working!
$ git clone https://github.com/jiachen247/llvm-sauce.git
$ cd llvm-sauce
$ yarn install
$ yarn build
After installation you can compile a Source program with the following command (substitute program.js
for the file containing the program to be compiled):
$ node ./dist/index.js program.js
Note the following available compiler flags:
-o FILE
,output=FILE
: Write LLVM target LLVM IR bytecode to a file specified byFILE
-t
,--tco
: Optimize tail recursive calls to respect ES6 PTC semantics-p
,--printAST
: Prints the Abstract Syntax Tree (AST) generated by the parser-h
,--help
:
For example, to turn on tail call optimization use the follow command:
$ node ./dist/index.js --tco program.js
Once you have compiled your program to LLVM IR, LLVM has two ways of running this IR by interpreting or compiling it further to machine code.
You can use lli
by running the following:
$ node ./dist/index.js program.js -o program.ll
$ lli program.ll
You can use llc
by running the following:
$ node ./dist/index.js program.js -o program.ll
$ llc program.ll -O3 -o program.s
$ g++ --std=c++11 -no-pie program.s -o program
$ ./program
You can specify -O1
, -O2
, or -O3
to specify the llc optimization level.
In this section is some information that might be helpful for developers who want to contribute to the project.
The ./src
folder is laid out in the following way:
├── codegen
│ ├── codegen.ts // main codegen entry point
│ ├── constants.ts
│ ├── expression // codegen for each expression grammar rule
│ │ ├── bop.ts
│ │ ├── call.ts
│ │ ├── identifier.ts
│ │ ├── literal.ts
│ │ ├── ternary.ts
│ │ └── uop.ts
│ ├── helper.ts // contains utility and shared helper functions
│ ├── statement // codegen for each statement grammar rule
│ │ ├── assignment.ts
│ │ ├── block.ts
│ │ ├── expression.ts
│ │ ├── function.ts
│ │ ├── ifelse.ts
│ │ ├── program.ts
│ │ └── return.ts
│ └── tailcall.ts // contains tail call optimizations
├── context
│ └── environment.ts
├── index.ts // entry point into the program
├── runtime // builds the compiler runtime
│ └── runtime.ts
└── types
└── types.ts
Test cases can be found under ./tests
with the source files as well as the generated IR. Tests consist comprehensive set of custom test cases we developed as well as examples from SICP chapter 1.
To run custom test cases, run the following:
$ yarn test
To generate LLVM IR for the custom test cases to be checked into github run:
$ yarn generate-ir
To run examples from chapter 1 of SICP (adapted from JS-Slang), run the following:
$ yarn test-sicp
To format all the TypeScript files under ./src
run:
$ yarn format