◀Table of Contents
Compiling to LLVM Bitcode
GraalVM can execute C/C++, Rust, and other languages that can be compiled to LLVM bitcode.
As the first step, you have to compile a program to LLVM bitcode using some LLVM compiler front end, for example, clang
for C and C++, rust
for the Rust programing language, etc.
File Format
While the GraalVM LLVM runtime can execute plain bitcode files, the preferred format is a native executable with embedded bitcode.
The executable file formats differ on Linux and macOS.
Linux by default uses ELF files.
The bitcode is stored in a section called .llvmbc
.
The macOS platform uses Mach-O files.
The bitcode is in the __bundle
section of the __LLVM
segment.
Using native executables with embedded bitcode offers two advantages over plain bitcode files.
First, build systems for native projects, for example a Makefile
, expect the result to be an executable.
Embedding the bitcode instead of changing the output format improves compatibility with existing projects.
Second, executables allow specifying library dependencies which is not possible with LLVM bitcode.
The GraalVM LLVM runtime utilizes this information to find and load dependencies.
LLVM Toolchain for Compiling C/C++
To simplify compiling C/C++ to executables with embedded bitcode, the LLVM runtime comes with a prebuilt LLVM toolchain.
The toolchain contains compilers such as clang
for C or clang++
for C++, but also other tools that are needed
for building native projects such as a linker (ld
), or an archiver (ar
) for creating static libraries.
- Get the location of the toolchain, using the
--print-toolchain-path
argument oflli
:./path/to/bin/lli --print-toolchain-path
- Set the
LLVM_TOOLCHAIN
environment variable:export LLVM_TOOLCHAIN=$(./path/to/bin/lli --print-toolchain-path)
- Then see the content of the toolchain path for a list of available tools:
ls $LLVM_TOOLCHAIN
Use those tools just as you would for native compilation. For example, save this C code in a file named hello.c
:
#include <stdio.h>
int main() {
printf("Hello from GraalVM!\n");
return 0;
}
Then you can compile hello.c
to an executable with embedded LLVM bitcode as follows:
$LLVM_TOOLCHAIN/clang hello.c -o hello
The resulting executable, hello
, can be executed on GraalVM using lli
:
$JAVA_HOME/bin/lli hello
External Library Dependencies
If the bitcode file depends on external libraries, GraalVM will automatically pick up the dependencies from the binary headers. For example:
#include <unistd.h>
#include <ncurses.h>
int main() {
initscr();
printw("Hello, Curses!");
refresh();
sleep(1);
endwin();
return 0;
}
This hello-curses.c file can be then compiled and run with:
$LLVM_TOOLCHAIN/clang hello-curses.c -lncurses -o hello-curses
lli hello-curses
Running C++
For running C++ code, the GraalVM LLVM runtime requires the libc++
standard library from the LLVM project.
The LLVM toolchain shipped with GraalVM automatically links against libc++
.
For example, save this code as a hello-c++.cpp file:
#include <iostream>
int main() {
std::cout << "Hello, C++ World!" << std::endl;
}
Compile it with clang++
shipped with GraalVM and execute:
$LLVM_TOOLCHAIN/clang++ hello-c++.cpp -o hello-c++
lli hello-c++
Hello, C++ World!
Running Rust
The LLVM toolchain, bundled with GraalVM, does not come with the Rust compiler. To install Rust, run the following in your command prompt, then follow the onscreen instructions:
curl https://sh.rustup.rs -sSf | sh
Save this example Rust code in a hello-rust.rs file:
fn main() {
println!("Hello Rust!");
}
This can be then compiled to bitcode with the --emit=llvm-bc
flag:
rustc --emit=llvm-bc hello-rust.rs
To run the Rust program, we have to tell GraalVM where to find the Rust standard libraries:
lli --lib $(rustc --print sysroot)/lib/libstd-* hello-rust.bc
Hello Rust!
Since the Rust compiler is not using the LLVM toolchain shipped with GraalVM, depending on the local Rust installation, an error similar to one of the following might happen:
Mismatching target triple (expected x86_64-unknown-linux-gnu, got x86_64-pc-linux-gnu)
Mismatching target triple (expected x86_64-apple-macosx10.11.0, got x86_64-apple-darwin)
This indicates that the Rust compiler used a different target triple than the LLVM toolchain shipped with GraalVM. In this particular case, the differences are just different naming conventions across Linux distributions or MacOS versions, there is no real difference. In that case, the error can be safely ignored:
lli --experimental-options --llvm.verifyBitcode=false --lib $(rustc --print sysroot)/lib/libstd-* hello-rust.bc
This option should only be used after manually verifying that the target triples are really compatible, i.e., the architecture, operating system, and C library all match.
For example, x86_64-unknown-linux-musl
and x86_64-unknown-linux-gnu
are really different, the bitcode is compiled for a different C library.
The --llvm.verifyBitcode=false
option disables all checks, GraalVM will then try to run the bitcode regardless, which might randomly fail in unexpected ways.