LLJVM provides a set of tools and libraries for running comparatively low level languages (such as C) on the JVM.
The C to JVM bytecode compilation provided by LLJVM involves several steps. Source code is first compiled to LLVM intermediate representation (IR) by a frontend such as
llvm-gcc or clang. LLVM IR is then translated to Jasmin assembly code, linked against other Java classes, and then assembled to JVM bytecode.
The use of LLVM IR as the intermediate representation allows more information about the source program to be preserved, compared to other methods which use MIPS binary as the intermediate representation (such as NestedVM and Cibyl). For example, functions are mapped to individual JVM methods, and all function calls are made with native JVM invocation instructions. This allows compiled code to be linked against arbitrary Java classes, and Java programs to natively call individual functions in the compiled code. It also allows programs to be split across multiple classes (comparable to dynamic linking), rather than statically linking everything into a single class.
Also note that while C is currently the only supported input language, any language with a compiler targeting LLVM IR could potentially be supported.
To quickly start translating your own programs you might want to use Docker. Getting LLJVM to run on modern machines (and keeping it running) can be quite tricky as it depends heavily on an old version of LLVM. As an alternative, Martin Haye has set up a virtual machine image here that is pre-configured for and includes LLJVM. Install Docker (you may need Boot2Docker) and start it this way:
docker run -i -v /your/project/dir:/project -t mhaye/lljvm /bin/bash
To compile LLJVM from source, follow the instructions below. If you have problems compiling from source and are running Linux on an i386-compatible platform, download the binary release, extract it, and download the runtime library into the resulting directory.
To compile LLJVM, simply call
make in the project root directory, and call
make check to run the testsuite.
To generate the the documentation for the java packages and the backend, call
make doc, which will generate HTML documentation in
doc/backend/ respectively. PDF documentation for the backend can be obtained by
make doc-pdf (requires PDFLaTeX), which will generate
doc/backend.pdf. A zip archive containing all of the documentation can be
created by calling
lljvm-cc frontend ties together several components to provide a C source
code to JVM class file compiler.
In order to use the frontend, either
clang must be installed.
llvm-gcc is recommended, and is used instead of
clang if both are available.
It is also required that the classpath contain jasmin.jar.
To compile a small number of source files to a class file, the following style of command can be used:
lljvm-cc foo.c bar.c -o foo
This will generate
foo.class, as well as a shell script
foo, which sets the
classpath appropriately and executes the classfile with the arguments passed to
it. This allows the class to be called in the same way as any other executable.
If calling the class without the shell script, note that the entire list of
arguments must be passed to the class, including argument 0 (the name of the
For a larger number of source files, it is often more efficient to separately compile each file to an object file as needed:
lljvm-cc -c foo.c
then link the object files together into a class file:
lljvm-cc -link foo.o bar.o baz.o -o foo
Note that the -link flag must be used if object files are being supplied instead of source files.
To link the object files together as a library instead of an executable,
-link-as-library can be passed instead of
-link. In this case the shell script
will not be generated.
To link against a library generated by
-l<name> flag can be used.
This will search for a library in the classpath called
Additional directories can be added to the classpath with the
Such additions to the classpath will also be added to the shell script, so
when linking an executable, the
-L flag must be passed for each directory
containing libraries needed by the executable (except the current directory and
directories in the system classpath).
An exception to the above is the
-lm flag. This will not search for
but will rather link against
should not be used as
libc is linked by default. If this is not desired, then
-nostdlib flag can be used.
By default all classes generated are placed in the default package. To assign
the class to a specific package, the
-classname flag can be used. For example,
the following command:
lljvm-cc ... -classname=com.example.foo -o bar/baz
will create a class named foo, in the package com.example, placing it in the
bar/com/example/. The shell script will be output to
-g3 flag is used, then Jasmin assembly with full debugging information
will be output to
In addition to the above flags, any flag accepted by
ld can also be
used. However, sometimes these flags may not be passed to the correct
component (this is a bug with
lljvm-cc and should be reported).
The frontend can also be used to compile
autoconf-based projects relatively
easily (although sometimes some changes to the project's build system may be
./configure CC=lljvm-cc LD='lljvm-cc -link' make CCLD='lljvm-cc -link'
To demonstrate the capabilities of LLJVM, several common software packages can
be compiled with
lljvm-cc by entering the
demo/ subdirectory and calling
make. To verify that all of these compiled correctly, call
A JAR archive containing all of the demo programs can be created by calling
make demo in the project root directory. For usage information, execute the
JAR in the project root directory with no arguments:
java -jar lljvm-demo.jar
The LLJVM Backend transforms LLVM IR into Jasmin assembly code. It can be invoked by:
lljvm-backend foo.bc > foo.j
There are two flags that are accepted by the backend:
lljvm-backend --help for more information.
The output file should then be linked by the LLJVM Linker (see below), and assembled into a class file by Jasmin:
java -jar jasmin.jar foo.j
The LLVJM Runtime has three components, the Core Runtime (
I/O Support Library (
lljvm.io), and the C Standard Library (
the JavaDoc documentation for further details on the former two. The latter is
Newlib compiled to JVM bytecode by
There are two command-line tools available: the linker, and the info utility. There are several ways to invoke these tools. The simplest way is to call them directly from the jar archive:
java -jar lljvm.jar <cmd> args...
lljvm.jar is already in the classpath, one of the following
can be used:
java lljvm.tools.Main <cmd> args... java lljvm.tools.<cmd>.Main args...
The LLJVM Linker qualifies references to external methods and fields in Jasmin
assembly code. At the top of the code (before any
.method directives), external
references should be specified through the
.extern pseudo-directive, such as:
.extern field foo I .extern method bar(I)V
Then the linker can be invoked by:
java -jar lljvm.jar ld LIBRARY... < INPUT.j > OUTPUT.j
For example, the following assembly code:
.extern method cos(D)D .extern field NULL I ... invokestatic cos(D)D getstatic NULL I CLASSFORMETHOD cos(D)D
linked with the command:
java -jar lljvm.jar ld java.lang.Math lljvm.runtime.Memory
... invokestatic java/lang/Math/cos(D)D getstatic lljvm/runtime/Memory/NULL I ldc "java/lang/Math"
The info utility lists the type signatures of the public static fields and
methods provided by a class. For example, to list those provided by
java -jar lljvm.jar info lljvm.lib.c
By default, any identifiers beginning with an underscore are omitted. To
disable this behaviour, pass the
java -jar lljvm.jar info -v lljvm.lib.c