This is an experimental package to compile Julia code to standalone libraries. A system image is not needed. It is also meant for cross compilation, so Julia code can be compiled for other targets, including WebAssembly and embedded targets.
using Pkg
Pkg.add(PackageSpec( url = "https://github.com/tshort/StaticCompiler.jl", rev = "master"))
using StaticCompiler
This package uses the LLVM package to generate code in the same fashion as CUDAnative.
Some of the key details of this approach are:
-
ccalls and cglobal -- When Julia compiles code CUDAnative style,
ccall
andcglobal
references get compiled to a direct pointer.StaticCompiler
converts these to symbol references for later linking. Forccall
with a tuple call to a symbol in a library,Cassette
is used to convert that to just a symbol reference (no dynamic library loading). -
Global variables -- A lot of code gets compiled with global variables, and these get compiled to a direct pointer.
StaticCompiler
includes a basic serialize/deserialize approach. Right now, this is fairly basic, and it takes shortcuts for some objects by swapping in wrong types. This can work because many times, the objects are not really used in the code. Finding the global variable can be a little tricky because the pointer is converted to a Julia object withunsafe_pointer_to_objref
, and that segfaults for some addresses. How to best handle cases like that is still to be determined. -
Initialization -- If libjulia is used, some init code needs to be run to set up garbage collection and other things. For this, a basic
blank.ji
file is used to feedjl_init_with_image
.
Long term, a better approach may be to use Julia's standard compilation techniques with "tree shaking" to generate a reduced system image (see here).
The API still needs work, but here is the general approach right now:
using StaticCompiler
m = irgen(cos, Tuple{Float64})
write(m, "cos.bc")
write_object(m, "cos.o")
cos.o
should contain a function called cos
. From there, you need to convert to link as needed with libjulia
.
See the test
directory for more information and types of code that currently run. The most advanced example that works is a call to an ODE solution using modified code from ODE.jl. For information on compiling and linking to an executable, see test/standalone-exe.jl.
-
It won't work for recursive code. Jameson's codegen-norecursion should fix that when merged.
-
cfunction
is not supported. -
Generic code that uses
jl_apply_generic
does not work. One strategy for this is to use Cassette to swap out known code that uses dynamic calls. Another approach is to write something likejl_apply_generic
to implement dynamic calls. -
The use of Cassette makes it more difficult for Julia to infer some things, and only type-stable code can be statically compiled with this approach.
-
It's only been tested on Linux and Windows.
Finally, this whole approach is young and likely brittle. Do not expect it to work for your code.