Running LLVM on GraalVM

GraalVM provides an implementation of the lli tool to directly execute programs from LLVM bitcode.

In contrast to static compilation that is normally used for LLVM based languages, LLI first interprets the bitcode and then dynamically compiles the hot parts of the program using the Graal compiler. This allows seamless interoperability with the dynamic languages supported by GraalVM.

Run programs in LLVM bitcode format:

lli [LLI Options] [GraalVM Options] [Polyglot Options] filename.bc [program args]

Where filename.bc is a single program source file in LLVM bitcode format.

Note: LLVM bitcode is platform dependent. The program must be compiled to bitcode for the appropriate platform.

Compiling to LLVM Bitcode

GraalVM can execute C/C++, Fortran, and other languages that can be compiled to LLVM bitcode. As a first step, you have to compile the program to LLVM bitcode using an LLVM frontend such as clang. C/C++ code can be compiled to LLVM bitcode using clang with the -emit-llvm option.

Here is some example C code named hello.c:

#include <stdio.h>

int main() {
    printf("Hello from GraalVM!\n");
    return 0;
}

You can compile hello.c to an LLVM bitcode file named hello.bc as follows:

$ clang -c -O1 -emit-llvm hello.c

You can then run hello.bc on GraalVM like this:

$ lli hello.bc
Hello from GraalVM!

External library dependencies

If the bitcode file depends on external libraries, they can be loaded using the --lib argument.

For example:

#include <unistd.h>
#include <ncurses.h>

int main() {
    initscr();
    printw("Hello, Curses!");
    refresh();
    sleep(1);
    endwin();
    return 0;
}

This can be run with:

$ clang -c -O1 -emit-llvm hello-curses.c
$ lli --lib /usr/lib/libncursesw.so hello-curses.bc

For Mac OS users, ncurses library path is /usr/lib/libncurses.dylib.

Running C++

For running C++ code, LLI requires the libc++ standard library from the LLVM project. If you are using Ubuntu Linux, install the package libc++1.

#include <iostream>

int main() {
    std::cout << "Hello, C++ World!" << std::endl;
}

Make sure you compile your C++ code with the correct standard library:

$ clang++ -c -O1 -emit-llvm -stdlib=libc++ hello-c++.cpp
$ lli hello-c++.bc
Hello, C++ World!

Running Rust

LLI doesn’t load the Rust standard libraries automatically. To install Rust, run the following in your terminal, then follow the onscreen instructions:

curl https://sh.rustup.rs -sSf | sh

To run Rust code, the required Rust libraries have to be specified manually.

fn main() {
    println!("Hello Rust!");
}

This can be run with:

$ rustc --emit=llvm-bc hello-rust.rs
$ lli --lib $(rustc --print sysroot)/lib/libstd-* hello-rust.bc
Hello Rust!

Interoperability

GraalVM supports several other programming languages, including JavaScript, Python 3, Ruby, and R. While LLI is designed to run LLVM bitcode, it also provides an API for programming language interoperability that lets you execute code from any other language that GraalVM supports.

Dynamic languages like JavaScript usually access object members by name. Since normally names are not preserved in LLVM bitcode, it must be compiled with debug info enabled.

The following example demonstrates how you can use the API for interoperability with other programming languages.

Let’s define a C struct for points and implement allocation functions:

// cpart.c
#include <polyglot.h>

#include <stdlib.h>
#include <stdio.h>

struct Point {
    double x;
    double y;
};

POLYGLOT_DECLARE_STRUCT(Point)

void *allocNativePoint() {
    struct Point *ret = malloc(sizeof(*ret));
    return polyglot_from_Point(ret);
}

void *allocNativePointArray(int length) {
    struct Point *ret = calloc(length, sizeof(*ret));
    return polyglot_from_Point_array(ret, length);
}

void freeNativePoint(struct Point *p) {
    free(p);
}

void printPoint(struct Point *p) {
    printf("Point<%f,%f>\n", p->x, p->y);
}

Compile this with:

$ clang -g -O1 -c -emit-llvm -I$GRAALVM_HOME/jre/languages/llvm cpart.c

You can access your C/C++ code from other languages like JavaScript:

// jspart.js

// Load and parse the LLVM bitcode into GraalVM
var cpart = Polyglot.evalFile("llvm" ,"cpart.bc");

// Allocate a light-weight C struct
var point = cpart.allocNativePoint();

// Access it as if it were a JS object
point.x = 5;
point.y = 7;

// Pass it back to a native function
cpart.printPoint(point);

// We can also allocate an array of structs
var pointArray = cpart.allocNativePointArray(15);

// We can access this array like it was a JS array
for (var i = 0; i < pointArray.length; i++) {
    var p = pointArray[i];
    p.x = i;
    p.y = 2*i;
}

cpart.printPoint(pointArray[3]);

// We can also pass a JS object to a native function
cpart.printPoint({x: 17, y: 42});

// Don't forget to free the unmanaged data objects
cpart.freeNativePoint(point);
cpart.freeNativePoint(pointArray);

Run this JavaScript file with:

$ js --polyglot jspart.js
Point<5.000000,7.000000>
Point<3.000000,6.000000>
Point<17.000000,42.000000>

See the Polyglot Reference and the Embedding documentation for more information about interoperability with other programming languages.

LLVM Compatibility

GraalVM works with LLVM bitcode versions 3.8 to 6.0.

Optimization flags

In contrast to the static compilation model of LLVM languages, in GraalVM the machine code is not directly produced from the LLVM bitcode, but there is an additional dynamic compilation step by the Graal compiler.

In this scenario, first the LLVM frontend (e.g. clang) does optimizations on the bitcode level, and then Graal does its own optimizations on top of that during dynamic compilation. Some optimizations are better when done ahead-of-time on the bitcode, while other optimizations are better left for the dynamic compilation of Graal, when profiling information is available.

In principle, all optimization levels should work, but for best results we suggest compiling the bitcode with optimization level -O1.

Cross-language interoperability will only work when the bitcode is compiled with debug information enabled (-g), and the -mem2reg optimization is performed on the bitcode (compiled with at least -O1, or explicitly using the opt tool).

LLI Command Options

-L <path>/--llvm.libraryPath=<path>: a list of paths where GraalVM will search for library dependencies. Paths are delimited by :.

--lib <libs>/--llvm.libraries=<libs>: a list of libraries to load. The list can contain precompiled native libraries (*.so/*.dylib) and bitcode libraries (*.bc). Files with a relative path are looked up relative to llvm.libraryPath. Entries are delimited by :.

--version prints the version and exit.

--version:graalvm prints GraalVM version information and exit.

Expert and Diagnostic Options

Use --help and --help:<topic> to get a full list of options.