LLVM:即时编译的简单示例

时间:2017-03-23 16:41:09

标签: c++ compilation llvm jit

我正在学习LLVM并尝试编译一个简单的函数:

int sum(int a, int b) {
    return a+b;
};

即时。

所以这是我到目前为止的代码:

#include <string>
#include <vector>
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Verifier.h"

using namespace llvm;

static LLVMContext &Context = getGlobalContext();
static std::unique_ptr<Module> MyModule = make_unique<Module>("my compiler", Context);

Function *createFunc(IRBuilder<> &Builder, std::string Name) {
    std::vector<Type*> Integers(2, Builder.getInt32Ty());
    auto *funcType = FunctionType::get(Builder.getInt32Ty(), Integers, false);
    auto *fooFunc = Function::Create(funcType, Function::ExternalLinkage, Name, MyModule.get());
    return fooFunc;
};

int main(int argc, char* argv[]) {
    static IRBuilder<> Builder(Context);
    auto *fooFunc = createFunc(Builder, "sum");
    auto *entry = BasicBlock::Create(Context, "entry", fooFunc);
    Builder.SetInsertPoint(entry);

    // Fill the function body
    auto args = fooFunc->arg_begin();
    Value *arg1 = &(*args);
    args = std::next(args);
    Value *arg2 = &(*args);
    auto *sum = Builder.CreateAdd(arg1, arg2, "tmp");
    Builder.CreateRet(sum);
    verifyFunction(*fooFunc);

    // TODO: compile and run it
    MyModule->dump();
    return 0;
}

这个编译,当我运行它时,我得到了预期的输出:

; ModuleID = 'my compiler'

define i32 @sum(i32, i32) {
entry:
  %tmp = add i32 %0, %1
  ret i32 %tmp
}

就像在教程中一样。

但是现在我想编译这个函数并从C ++运行它。我正在寻找最简单的方法来做这样的事情:

auto compiledStuff = ...;
auto compiledFn = (int (*)(int, int))compiledStuff;
auto result = compiledFn(3, 8);

我一直在挖掘the official Kaleidoscope tutorial,但JIT教程非常复杂,似乎专注于优化和懒惰,而我仍然无法弄清楚如何轻松编译模块并调用从它起作用。

任何帮助?

2 个答案:

答案 0 :(得分:1)

所以,我已经挖掘了KaleidoscopeJIT并找回了最重要的部分。首先请注意我使用的是 llvm-4.0 。我没有意识到4.0和更低版本之间的不兼容性,我有很多问题。

该代码适用于C ++ 11。我使用 clang ++ - 4.0 和以下编译标志:

llvm-config-4.0 --cxxflags --ldflags --system-libs --libs core engine

现在整个代码:

#include <string>
#include <vector>
#include <iostream>
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Verifier.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
#include "llvm/ExecutionEngine/RuntimeDyld.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
#include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Mangler.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/TargetSelect.h"

using namespace llvm;
typedef orc::ObjectLinkingLayer<> ObjLayerT;
typedef orc::IRCompileLayer<ObjLayerT> CompileLayerT;

static LLVMContext Context;
static auto MyModule = make_unique<Module>("my compiler", Context);

Function *createFunc(IRBuilder<> &Builder, std::string Name) {
    std::vector<Type*> Integers(2, Builder.getInt32Ty());
    auto *funcType = FunctionType::get(Builder.getInt32Ty(), Integers, false);
    auto *fooFunc = Function::Create(funcType, Function::ExternalLinkage, Name, MyModule.get());
    return fooFunc;
};

void updateBody(Function *fooFunc, IRBuilder<> &Builder) {
    auto *entry = BasicBlock::Create(Context, "entry", fooFunc);
    Builder.SetInsertPoint(entry);
    auto args = fooFunc->arg_begin();
    Value *arg1 = &(*args);
    args = std::next(args);
    Value *arg2 = &(*args);
    auto *sum = Builder.CreateAdd(arg1, arg2, "tmp");
    Builder.CreateRet(sum);
};

int main(int argc, char* argv[]) {
    // Prepare the module
    static IRBuilder<> Builder(Context);
    auto *fooFunc = createFunc(Builder, "sum");
    updateBody(fooFunc, Builder);
    verifyFunction(*fooFunc);

    // Initilaze native target
    InitializeNativeTarget();
    InitializeNativeTargetAsmPrinter();
    InitializeNativeTargetAsmParser();

    // Prepare jit layer
    ObjLayerT ObjectLayer;
    std::unique_ptr<TargetMachine> TM(EngineBuilder().selectTarget());
    DataLayout DL(TM->createDataLayout());
    CompileLayerT CompileLayer(ObjectLayer, orc::SimpleCompiler(*TM));
    auto Resolver = orc::createLambdaResolver(
        [&](const std::string &Name) {
            if (auto Sym = CompileLayer.findSymbol(Name, false))
                return Sym;
            return JITSymbol(nullptr);
        },
        [](const std::string &S) { return nullptr; }
    );

    // Add MyModule to the jit layer
    std::vector<std::unique_ptr<Module>> Modules;
    Modules.push_back(std::move(MyModule));
    CompileLayer.addModuleSet(
        std::move(Modules),
        make_unique<SectionMemoryManager>(),
        std::move(Resolver)
    );

    // Retrieve the foo symbol
    std::string MangledName;
    raw_string_ostream MangledNameStream(MangledName);
    Mangler::getNameWithPrefix(MangledNameStream, "sum", DL);
    auto Sym = CompileLayer.findSymbol(MangledNameStream.str(), true);

    // Cast to function
    auto func = (int(*)(int, int))Sym.getAddress();

    // Try it
    std::cout << func(5, 7) << std::endl;

    return 0;
}

我不确定是否需要所有包含但无论如何它都像魅力一样。虽然我期待着对如何改进它的任何评论。 :)

答案 1 :(得分:0)

使用提供的KaleidoscopeJIT.h这非常简单(我在这个例子中使用LLVM 4.0.0):

// Your existing includes here.
#include "llvm/Support/TargetSelect.h" // For InitializeNativeTarget() etc.
#include "KaleidoscopeJIT.h" 

int main() {
    // Your existing main body here.

    InitializeNativeTarget();
    InitializeNativeTargetAsmPrinter();
    InitializeNativeTargetAsmParser();

    orc::KaleidoscopeJIT jit;
    MyModule->setDataLayout(jit.getTargetMachine().createDataLayout());
    auto moduleHandle = jit.addModule(std::move(MyModule)); // JIT-compile MyModule.
    auto symbol = jit.findSymbol("sum"); // Get the compiled sum function.
    auto sumFunc = (int(*)(int, int)) symbol.getAddress(); // Cast it.
    auto result = sumFunc(42, 42); // Call it.
    assert(result == 84); // Voilà.
}