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 StaticCompilerThis 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,
ccallandcglobalreferences get compiled to a direct pointer.StaticCompilerconverts these to symbol references for later linking. Forccallwith a tuple call to a symbol in a library,Cassetteis 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.
StaticCompilerincludes 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.jifile 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.
-
cfunctionis not supported. -
Generic code that uses
jl_apply_genericdoes 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_genericto 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.