I'm writing code to generate LLVM bytecode from a custom VM bytecode using the LLVM Go bindings; the code is then JITed and executed in-process.
The custom VM bytecode has several operations that can't be implemented directly in LLVM, because they require changing external state; the functionality for these opcodes is implemented as Go functions.
There are excellent guides on getting started with generating LLVM bytecode from Go, but none address the issue of callbacks or exported functions. Is it possible to generate LLVM instructions to call back into the Go functions? If so, how?
I've tried the way described below by @arrowd, and it doesn't appear to work. Source code, adapted from Felix Angell's blog post:
package main
import (
"C"
"fmt"
"llvm.org/llvm/final/bindings/go/llvm"
)
// export AddInts
func AddInts(arg1, arg2 int) int {
return arg1 + arg2;
}
func main() {
// setup our builder and module
builder := llvm.NewBuilder()
mod := llvm.NewModule("my_module")
// create our function prologue
main := llvm.FunctionType(llvm.Int32Type(), []llvm.Type{}, false)
llvm.AddFunction(mod, "main", main)
block := llvm.AddBasicBlock(mod.NamedFunction("main"), "entry")
builder.SetInsertPoint(block, block.FirstInstruction())
// int a = 32
a := builder.CreateAlloca(llvm.Int32Type(), "a")
builder.CreateStore(llvm.ConstInt(llvm.Int32Type(), 32, false), a)
// int b = 16
b := builder.CreateAlloca(llvm.Int32Type(), "b")
builder.CreateStore(llvm.ConstInt(llvm.Int32Type(), 16, false), b)
// return a + b
bVal := builder.CreateLoad(b, "b_val")
aVal := builder.CreateLoad(a, "a_val")
addIntsType := llvm.FunctionType(llvm.Int32Type(), []llvm.Type{llvm.Int32Type(), llvm.Int32Type()}, false)
addInts := llvm.AddFunction(mod, "AddInts", addIntsType)
call := builder.CreateCall(addInts, []llvm.Value{aVal, bVal}, "AddInts")
builder.CreateRet(call)
// verify it's all good
if ok := llvm.VerifyModule(mod, llvm.ReturnStatusAction); ok != nil {
fmt.Println(ok.Error())
}
mod.Dump()
// create our exe engine
engine, err := llvm.NewExecutionEngine(mod)
if err != nil {
fmt.Println(err.Error())
}
// run the function!
funcResult := engine.RunFunction(mod.NamedFunction("main"), []llvm.GenericValue{})
fmt.Printf("%d\n", funcResult.Int(false))
}
Returns:
; ModuleID = 'my_module'
define i32 @main() {
entry:
%a = alloca i32
store i32 32, i32* %a
%b = alloca i32
store i32 16, i32* %b
%b_val = load i32* %b
%a_val = load i32* %a
%AddInts = call i32 @AddInts(i32 %a_val, i32 %b_val)
ret i32 %AddInts
}
declare i32 @AddInts(i32, i32)
LLVM ERROR: Tried to execute an unknown external function: AddInts
exit status 1