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.
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.
To simplify compiling C/C++ to executables with embedded bitcode, GraalVM comes with a pre-built 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.
The LLVM toolchain can be added to GraalVM on demand with the GraalVM Updater tool:
$GRAALVM_HOME/bin/gu install llvm-toolchain
The above command will install the LLVM toolchain from the GitHub catalog for GraalVM Community users. For GraalVM Enterprise users, the manual installation is required.
To get the location of the toolchain, use the --print-toolchain-path
argument of lli
:
export LLVM_TOOLCHAIN=$($GRAALVM_HOME/bin/lli --print-toolchain-path)
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
:
$GRAALVM_HOME/bin/lli hello
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
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!
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!