Skip to content

Commit 3f9f236

Browse files
authored
Major cleanup. Remove compile, compile_wasm, and MixTape (#146)
* Remove `compile`, do some cleanup * update README * note about `compile_shlib` * note about compile_shlib * remove compile_wasm and MixTape; allow specifying a method_table * Forgot v1.8 doesn't have package extensions * oops * don't export `compile_wasm`
1 parent 1979264 commit 3f9f236

15 files changed

+294
-1285
lines changed

.github/workflows/ci-integration.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ jobs:
2020
version:
2121
- '1.8'
2222
- '1.9'
23+
- '1.10.0-rc1'
2324
os:
2425
- ubuntu-latest
2526
- macOS-latest

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ jobs:
2020
version:
2121
- '1.8'
2222
- '1.9'
23+
- '1.10.0-rc1'
2324
os:
2425
- ubuntu-latest
2526
- macOS-latest

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "StaticCompiler"
22
uuid = "81625895-6c0f-48fc-b932-11a18313743c"
33
authors = ["Tom Short and contributors"]
4-
version = "0.5.3"
4+
version = "0.6"
55

66
[deps]
77
Clang_jll = "0ee61d77-7f21-5576-8119-9fcc46b10100"
@@ -17,7 +17,7 @@ StaticTools = "86c06d3c-3f03-46de-9781-57580aa96d0a"
1717

1818
[compat]
1919
CodeInfoTools = "0.3"
20-
GPUCompiler = "0.21"
20+
GPUCompiler = "0.21, 0.22, 0.23, 0.24"
2121
LLVM = "6"
2222
MacroTools = "0.5"
2323
StaticTools = "0.8"

README.md

Lines changed: 37 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -15,39 +15,8 @@ using Pkg
1515
Pkg.add("StaticCompiler")
1616
```
1717

18-
There are two main ways to use this package:
19-
20-
### Linked compilation
21-
The first option is via the `compile` function, which can be used when you want to compile a Julia function for later use from within Julia:
22-
```julia
23-
julia> using StaticCompiler
24-
25-
julia> fib(n) = n <= 1 ? n : fib(n - 1) + fib(n - 2)
26-
fib (generic function with 1 method)
27-
28-
julia> fib_compiled, path = compile(fib, Tuple{Int}, "fib")
29-
(f = fib(::Int64) :: Int64, path = "fib")
30-
31-
julia> fib_compiled(10)
32-
55
33-
```
34-
Now we can quit this session and load a new one where `fib` is not defined:
35-
```julia
36-
julia> using StaticCompiler
37-
38-
julia> fib
39-
ERROR: UndefVarError: fib not defined
40-
41-
julia> fib_compiled = load_function("fib")
42-
fib(::Int64) :: Int64
43-
44-
julia> fib_compiled(10)
45-
55
46-
```
47-
See the file `tests/runtests.jl` for some examples of functions that work with `compile` (and some that don't, marked with `@test_skip`).
48-
4918
### Standalone compilation
50-
The second way to use this package is via the `compile_executable` and `compile_shlib` functions, for when you want to compile a Julia function to a native executable or shared library for use from outside of Julia:
19+
StaticCompiler.jl provides the functions `compile_executable` and `compile_shlib` for compiling a Julia function to a native executable or shared library for use from outside of Julia:
5120
```julia
5221
julia> using StaticCompiler, StaticTools
5322

@@ -63,25 +32,49 @@ shell> ls -alh hello
6332
shell> ./hello
6433
Hello, world!
6534
```
66-
This latter approach comes with substantially more limitations, as you cannot rely on `libjulia` (see, e.g., [StaticTools.jl](https://github.com/brenhinkeller/StaticTools.jl) for some ways to work around these limitations).
35+
This approach comes with substantial limitations compared to regular julia code, as you cannot rely on julia's runtime, `libjulia` (see, e.g., [StaticTools.jl](https://github.com/brenhinkeller/StaticTools.jl) for some ways to work around these limitations).
36+
37+
The low-level function `StaticCompiler.generate_obj` (not exported) generates object files. This can be used for more control of compilation. This can be used for example, to cross-compile to other targets.
38+
39+
### Method overlays
6740

68-
The low-level function `StaticCompiler.generate_obj` (not exported) generates object files. This can be used for more control of compilation. This can be used to cross-compile to other targets.
41+
Sometimes, a julia function you want to statically compile will do things (such as throwing errors) that aren't supported natively by StaticCompiler. One tool provided for working around this is the `@device_override` macro which lets you swap out a method, but only inside of a StaticCompiler.jl compilation context. For example:
6942

70-
### Mixtape
43+
```julia
44+
julia> using Libdl, StaticCompiler
45+
46+
julia> f(x) = g(x) + 1;
47+
48+
julia> g(x) = 2x
49+
50+
julia> @device_override g(x::Int) = x - 10
7151

72-
This feature allows one to change functionality when statically compiling. This uses code and API from [Mixtape](https://github.com/JuliaCompilerPlugins/Mixtape.jl) to transform lowered code much like [Cassette](https://github.com/JuliaLabs/Cassette.jl).
52+
julia> f(1) # Gives the expected answer in regular julia
53+
3
54+
55+
julia> dlopen(compile_shlib(f, (Int,), "./")) do lib
56+
fptr = dlsym(lib, "f")
57+
# Now use the compiled version where + is replaced with -
58+
@ccall $fptr(1::Int)::Int
59+
end
60+
-8
61+
```
62+
Typically, errors should be overrided and replaced with `@print_and_throw`, which is StaticCompiler friendly, i.e.
63+
we define overrides such as
64+
``` julia
65+
@device_override @noinline Base.Math.throw_complex_domainerror(f::Symbol, x) =
66+
@print_and_throw c"This operation requires a complex input to return a complex result"
67+
```
7368

74-
To use the Mixtape feature, define a `CompilationContext` struct and pass this to any of the compilation functions with the `mixtape` keyword. Define `transform` and `allow` functions for this `CompilationContext` to define the transformation to be done.
69+
If for some reason, you wish to use a different method table (defined with `Base.Experimental.@MethodTable` and `Base.Experimental.@overlay`) than the default one provided by StaticCompiler.jl, you can provide it to `compile_executable` and `compile_shlib` via a keyword argument `method_table`.
7570

76-
See [here](https://github.com/tshort/StaticCompiler.jl/blob/master/test/testintegration.jl#L329) for an example.
7771

7872
## Approach
7973

8074
This package uses the [GPUCompiler package](https://github.com/JuliaGPU/GPUCompiler.jl) to generate code.
8175

8276
## Limitations
8377

84-
* GC-tracked allocations and global variables do work with `compile`, but the way they are implemented is brittle and can be dangerous. Allocate with care.
8578
* GC-tracked allocations and global variables do *not* work with `compile_executable` or `compile_shlib`. This has some interesting consequences, including that all functions _within_ the function you want to compile must either be inlined or return only native types (otherwise Julia would have to allocate a place to put the results, which will fail).
8679
* Since error handling relies on libjulia, you can only throw errors from standalone-compiled (`compile_executable` / `compile_shlib`) code if an explicit overload has been defined for that particular error with `@device_override` (see [quirks.jl](src/quirks.jl)).
8780
* Type instability. Type unstable code cannot currently be statically compiled via this package.
@@ -97,29 +90,24 @@ To enable code to be statically compiled, consider the following:
9790

9891
* Avoid Julia's internal allocations. That means don't bake in use of Arrays or Strings or Dicts. Types from StaticTools can help, like StaticStrings and MallocArrays.
9992

100-
* If need be, manage memory manually, using `malloc` and `free`. This works with StaticTools.MallocString and StaticTools.MallocArray.
93+
* If need be, manage memory manually, using `malloc` and `free` from StaticTools.jl. This works with `StaticTools.MallocString` and `StaticTools.MallocArray`, or use [Bumper.jl](https://github.com/MasonProtter/Bumper.jl).
10194

10295
* Don't use global variables that need to be allocated and initialized. Instead of global variables, use context structures that have an initialization function. It is okay to use global Tuples or NamedTuples as the use of these should be baked into compiled code.
10396

10497
* Use context variables to store program state, inputs, and outputs. Parameterize these typese as needed, so your code can handle normal types (Arrays) and static-friendly types (StaticArrays, MallocArrays, or StrideArrays). The SciML ecosystem does this well ([example](https://github.com/SciML/OrdinaryDiffEq.jl/blob/e7f045950615352ddfcb126d13d92afd2bad05e4/src/integrators/type.jl#L82)). Use of these context variables also enables allocations and initialization to be centralized, so these could be managed by the calling routines in Julia, Python, JavaScript, or other language.
10598

106-
* If your code needs an array as a workspace, instead of directly creating it, create it as a function argument (where it could default to a standard array creation). That code could be statically compiled if that function argument is changed to a MallocArray or another static-friendly alternative.
99+
* Arguments and returned values from `compile_shlib` must be native objects such as `Int`, `Float64`, or `Ptr`. They cannot be things like `Tuple{Int, Int}` because that is not natively sized. Such objects need to be passed by reference instead of by value.
107100

108-
* Use [Bumper.jl](https://github.com/MasonProtter/Bumper.jl) to avoid allocations in some loops.
101+
* If your code needs an array as a workspace, instead of directly creating it, create it as a function argument (where it could default to a standard array creation). That code could be statically compiled if that function argument is changed to a MallocArray or another static-friendly alternative.
109102

110103
## Guide for Statically Compiling Code
111104

112-
If you're trying to statically compile generic code, you may run into issues if that code uses features not supported by StaticCompiler. One option is to change the code you're calling using the tips above. If that is not easy, you may by able to compile it anyway. One option is to use method overrides to change what methods are called. Another option is to use the Mixtape feature to change problematic code as part of compilation. For example, you could convert all Strings to StaticStrings.
105+
If you're trying to statically compile generic code, you may run into issues if that code uses features not supported by StaticCompiler. One option is to change the code you're calling using the tips above. If that is not easy, you may by able to compile it anyway. One option is to use method overlays to change what methods are called.
113106

114107
[Cthulhu](https://github.com/JuliaDebug/Cthulhu.jl) is a great help in digging into code, finding type instabilities, and finding other sources of code that may break static compilation.
115108

116109
## Foreign Function Interfacing
117110

118111
Because Julia objects follow C memory layouts, compiled libraries should be usable from most languages that can interface with C. For example, results should be usable with Python's [CFFI](https://cffi.readthedocs.io/en/latest/) package.
119112

120-
For WebAssembly, interface helpers are available at [WebAssemblyInterfaces](https://github.com/tshort/WebAssemblyInterfaces.jl).
121-
122-
123-
124-
125-
113+
For WebAssembly, interface helpers are available at [WebAssemblyInterfaces](https://github.com/tshort/WebAssemblyInterfaces.jl), and users should also see [WebAssemblyCompiler](https://github.com/tshort/WebAssemblyCompiler.jl) for a package more focused on compilation of WebAssebly in general.

0 commit comments

Comments
 (0)