You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
-
49
18
### 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:
51
20
```julia
52
21
julia>using StaticCompiler, StaticTools
53
22
@@ -63,25 +32,49 @@ shell> ls -alh hello
63
32
shell>./hello
64
33
Hello, world!
65
34
```
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
67
40
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:
69
42
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_overrideg(x::Int) = x -10
71
51
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.
@print_and_throwc"This operation requires a complex input to return a complex result"
67
+
```
73
68
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`.
75
70
76
-
See [here](https://github.com/tshort/StaticCompiler.jl/blob/master/test/testintegration.jl#L329) for an example.
77
71
78
72
## Approach
79
73
80
74
This package uses the [GPUCompiler package](https://github.com/JuliaGPU/GPUCompiler.jl) to generate code.
81
75
82
76
## Limitations
83
77
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.
85
78
* 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).
86
79
* 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)).
87
80
* 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:
97
90
98
91
* 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.
99
92
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).
101
94
102
95
* 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.
103
96
104
97
* 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.
105
98
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.
107
100
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.
109
102
110
103
## Guide for Statically Compiling Code
111
104
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.
113
106
114
107
[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.
115
108
116
109
## Foreign Function Interfacing
117
110
118
111
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.
119
112
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