by: Tim Besard (GitHub: maleadt
)
Julia wrapper of the LLVM C API:
Supports LLVM 3.9+, Julia 0.5+
CUDAnative.jl needs LLVM to:
Move PTX back-end out of codegen → birth of LLVM.jl
Why not target the C++ API directly, using Cxx.jl?
High-level, "Julian" API
Build your own compiler
Generate IR for Julia
Optimize or compile code generated by Julia
...
Pkg.add("LLVM")
Linux, macOS (unsupported)
Julia source build: llvm-config
, headers, same host compiler ...
LLVM_ASSERTIONS=1
using LLVM
mod = LLVM.Module("demo")
context(mod) == GlobalContext()
Context() do ctx
mod = LLVM.Module("temp_module", ctx)
# ...
dispose(mod)
end
No automatic collection, use do
blocks or call dispose
!
When emitting code for Julia, use the appropriate context:
jlctx = LLVM.Context(cglobal(:jl_LLVMContext, Void));
Let's create a function:
sum(::Int32, ::Int32)::Int32
param_types = [LLVM.Int32Type(), LLVM.Int32Type()]
ret_type = LLVM.Int32Type();
LLVM types are created by functions, bound to a context.
fun_type = LLVM.FunctionType(ret_type, param_types)
sum = LLVM.Function(mod, "sum", fun_type)
mod
builder = Builder();
Builder() do builder
# ...
end
bb = BasicBlock(sum, "entry")
position!(builder, bb)
tmp = add!(builder, parameters(sum)[1], parameters(sum)[2], "tmp")
ret!(builder, tmp)
dispose(builder)
mod
verify(mod)
ir = convert(String, mod);
engine = Interpreter(mod);
args = [GenericValue(LLVM.Int32Type(), 1),
GenericValue(LLVM.Int32Type(), 2)];
res = LLVM.run(engine, sum, args);
convert(Int, res)
dispose.(args)
dispose(res)
dispose(engine)
mod
has been consumed by the ExecutionEngine
:
mod
println(ir)
mod = parse(LLVM.Module, ir, jlctx)
context(mod) == jlctx
sum = get(functions(mod), "sum")
call_sum(x, y) = Base.llvmcall(LLVM.ref(sum), Int32,
Tuple{Int32, Int32},
convert(Int32,x), convert(Int32,y))
call_sum(Int32(1),Int32(2))
@code_llvm call_sum(Int32(1),Int32(2))
push!(function_attributes(sum), EnumAttribute("alwaysinline"))
@code_llvm call_sum(Int32(1),Int32(2))
ctx = context(mod)
Builder() do builder
bb = first(blocks(sum))
inst = first(instructions(bb))
position!(builder, inst)
alloca!(builder, LLVM.Int32Type(ctx), "unused")
end
sum
Always start with a pass manager:
mpm = ModulePassManager();
ModulePassManager() do mpm
# ...
end
aggressive_dce!(mpm)
run!(mpm, mod)
mod
You can populate a pass manager using a PassManagerBuilder
:
PassManagerBuilder() do pmb
optlevel!(pmb, 0)
sizelevel!(pmb, 0)
populate!(mpm, pmb)
run!(mpm, mod)
end
function runOnFunction(fn::LLVM.Function)
println("Processing $(name(fn))")
return false
end
pass = FunctionPass("DummyFunctionPass", runOnFunction);
FunctionPassManager(mod) do fpm
add!(fpm, pass)
run!(fpm, sum)
end