1. 程式人生 > >LLVM與C++程式碼的相互呼叫(全註釋)

LLVM與C++程式碼的相互呼叫(全註釋)

一、在C++中呼叫LLVM編寫的IR函式

#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/CodeGen/MachineCodeInfo.h"
#include "llvm/Analysis/Verifier.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/IR/DerivedTypes.h"
#include <iostream>
/*
      務必使用llvm-3.3
*/
using namespace llvm;
using namespace std;
int main()
{
    int x = 9;
    // 獲取LLVM上下文
    LLVMContext &context = llvm::getGlobalContext();
    //獲取LLVM Module,Module類似於一個完整的.cc檔案
    Module *module = new Module("test", context);
    //獲取一個函式,如果這個函式已經宣告,獲取相應的函式指標,如果沒有,宣告
    Function *f = cast<Function>(module->getOrInsertFunction("foo", Type::getInt32Ty(context),
                                                             Type::getInt32Ty(context), NULL));
    //建立一個程式碼塊,這個程式碼塊是之前宣告的函式foo的。程式碼塊就是C++中的 {}
    BasicBlock *BB = BasicBlock::Create(context, "entryBar", f);
    //建立LLVM Builder,Builder的作用就是輔助完成IR程式碼,這個builder用於在程式碼塊BB中完成IR
    IRBuilder<> builder(BB);
    //下面是foo中的主要邏輯:int ret=x;return ret;
    Value *ret = builder.getInt32(x);
    builder.CreateRet(ret);
    //這行程式碼的作用是引入初始化native,JIT會將IR編譯成當前主機可以執行的二進位制程式碼,而native中包含了當前主機的作業系統資訊
    InitializeNativeTarget();
    //建立執行JIT引擎,並將IR程式碼放入,也就是放入module(因為IR程式碼都在其中)
    ExecutionEngine *ee = EngineBuilder(module).setEngineKind(EngineKind::JIT).create();
    //從執行引擎中獲取相應的函式指標,JIT即時編譯器後我們之前寫的IR函式可以通過函式指標獲取
    void *barAddr = ee->getPointerToFunction(f);
    //將函式指標進行強轉
    typedef int32_t (*FuncType)(int32_t);
    FuncType barFunc = (FuncType)barAddr;
    //正式呼叫經由JIT編譯後的IR函式
    int64_t n = 9;
    cout << barFunc(n) << endl;
    return 0;
}

二、在LLVM編寫的IR函式中呼叫C++程式碼

#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/CodeGen/MachineCodeInfo.h"
#include "llvm/Analysis/Verifier.h"
#include <iostream>

using namespace llvm;

/*
    務必使用llvm-3.3
*/
int foo(int x)
{
    return 2 * x;
}

int value = 10;

int main()
{
    LLVMContext &context = llvm::getGlobalContext();
    Module *module = new Module("test", context);

    //在IR程式碼中宣告一個全域性變數
    GlobalVariable *v = cast<GlobalVariable>(module->getOrInsertGlobal("value", Type::getInt32Ty(context)));
    //在IR中宣告一個函式,注意我們並不會在IR中定義foo,我們會將這個IR中宣告的函式對映到C++程式碼中的函式
    Function *f = cast<Function>(module->getOrInsertFunction("foo", Type::getInt32Ty(context),
                                                             Type::getInt32Ty(context), NULL));

    //在IR中宣告一個函式bar,我們會用IR定義這個函式
    Function *bar = cast<Function>(module->getOrInsertFunction("bar", Type::getInt32Ty(context), NULL));

    //建立函式bar的程式碼塊
    BasicBlock *entry = BasicBlock::Create(context, "entry", bar);
    IRBuilder<> builder(entry);

    //用一個區域性變數獲取全域性變數v的值
    Value *v_IR = builder.CreateLoad(v);
    //呼叫函式foo
    Value *ret = builder.CreateCall(f, v_IR);
    //返回值
    builder.CreateRet(ret);

    InitializeNativeTarget();
    ExecutionEngine *ee = EngineBuilder(module).setEngineKind(EngineKind::JIT).create();

    //將外部的C++程式碼中的全域性變數對映到IR程式碼中,IR程式碼中只有宣告
    ee->addGlobalMapping(v, &value);

    //將外部的C++程式碼中的全域性函式對映到IR程式碼中,IR程式碼中只有宣告
    ee->addGlobalMapping(f, (void *)foo);

    void *barAddr = ee->getPointerToFunction(bar);
    typedef int (*FuncType)();
    FuncType barFunc = (FuncType)barAddr;

    std::cout << barFunc() << std::endl;
    return 0;
}