How can JITed LLVM code return to the Go function?

I am writing code to generate LLVM bytecode from VM user bytecode using LLVM Go bindings ; the code is then JITed and executed in the process.

The VM user bytecode has several operations that cannot be implemented directly in LLVM, since they require an external state change; the functionality for these opcodes is implemented as Go functions.

great start-up guides when creating LLVM bytecode from Go, but not a single issue is related to callbacks or exported functions. Can LLVM statements be generated to return to Go functions? If so, how?

I tried the method described below, @arrowd, and it does not seem to work. Source code adapted from a Felix Angell 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 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))
}

Return:

; 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
+4
source share
2 answers

The main problem is that you are not actually using JIT here, you are using an interpreter. llvm.NewExecutionEnginewill build the JIT compiler, if available, and otherwise return to the interpreter.

You must use llvm.NewMCJITCompiler. This will crash without further changes for the same reason why NewExecutionEngine did not provide JIT. You need to add the following to your "func main ()":

llvm.LinkInMCJIT()               
llvm.InitializeNativeTarget()    
llvm.InitializeNativeAsmPrinter()

, , () "//export".

+2

Go C, "export". , LLVM JIT . C, , , goProgressCB(args);, CallInst JIT.

.

. , Go C, . clang -S -emit-llvm clang -march=cpp IR ++ LLVM API, .

+3

All Articles