2016-04-26 6 views
4

私は、LLVM Go bindingsを使ってカスタムVMバイトコードからLLVMバイトコードを生成するコードを書いています。コードがJITされ、インプロセスで実行されます。JITed LLVMコードはGo関数にどのようにコールバックできますか?

カスタムVMバイトコードには、外部状態を変更する必要があるため、LLVMで直接実装できない操作がいくつかあります。これらのオペコードの機能は、Go機能として実装されています。

GoからLLVMバイトコードを生成することを開始するにあたり、excellent guidesがありますが、コールバックまたはエクスポートされた関数の問題に対処するものはありません。 Go関数をコールバックするLLVM命令を生成することは可能ですか?もしそうなら、どうですか?

下記の@arrowdの方法を試しましたが、動作していません。フェリックス・エンジェルのブログ記事から適応ソースコード、:

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)) 
} 

戻り値:

; 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 
+0

HaskellはコードをC関数としてエクスポートする方法を提供しています。そのため、HaskellをLLVMを含む何かから呼び出すことが可能です。 Goにはこのような機能がありますか? – arrowd

+0

@arrowd私の理解では、CGoはこれを提供しています(ネイティブのGo実装では独自の呼び出し規約を使用していますが)。これは正しい方向への一歩ですが、実際にLLVMからその関数を呼び出す方法はまだわかりません。 –

答えて

2

主な問題は、ここでJITを実際に使用していないことです。インタープリタを使用しています。 llvm.NewExecutionEngineは、使用可能な場合はJITコンパイラを作成し、そうでない場合はインタープリタにフォールバックします。

llvm.NewMCJITCompilerを使用してください。これは、NewExecutionEngineが最初にJITを生成しなかったのと同じ理由で、追加の変更なしで失敗します。あなたの「FUNCメイン()」に以下を追加する必要があります。

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

もう、あなたのコードで小さい、問題が空白(の欠如)が「//輸出」の魔法に重要であるということです。

+0

素晴らしい - ありがとう! –

3

this answerによると、あなたは「輸出」にコメントを追加することにより、Cのものとして囲碁関数をエクスポートすることができます。この機能を実行する場合、LLVM JITは実行時リンク時にこのシンボルを解決できる必要があります。答えは、エクスポートされた関数を呼び出すCコードを提供しませんでしたが、それがちょうどgoProgressCB(args);であると仮定すると、CallInstをこの関数に作成してJITできます。

更新されました。

this answerをご覧ください。最近のGoでは、Goを呼び出すためのCヘッダーを生成できるようです。これにより、clang -S -emit-llvmまたはclang -march=cppでコンパイルし、呼び出し命令を構成するLLVM APIへのテキストIRまたはC++呼び出しを取得することができます。

+0

ありがとう!私はまだ誰かが単純なエンドツーエンドのLLVMの例を思いつくことを望んでいる(部分的には私がこれを最後に頼む人ではないと確信している)。 –

+0

ちなみに、goのLLVMバインディングには 'CallInst'がありません。私が探しているはずのものと同等のものがありますか? –

+0

残念ながら、これはうまくいかないようです。ポストへの私の更新を見てください。 –

関連する問題