duanqinjiao5244 2016-04-26 16:02
浏览 95
已采纳

JITed LLVM代码如何回调Go函数?

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
", 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
  • 写回答

2条回答 默认 最新

  • doucou19961205 2016-04-27 01:01
    关注

    The main problem is that you're not actually using the JIT here, you're using the interpreter. llvm.NewExecutionEngine will construct a JIT compiler if it is available, and fall back to the interpreter otherwise.

    You should use llvm.NewMCJITCompiler. This will fail without additional changes, for the same reason why NewExecutionEngine did not yield a JIT in the first place. You need to add the following to your "func main()":

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

    Another, smaller, issue with your code is that (lack of) whitespace is important in the "//export" magic.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?