Я изучаю 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);
Я копался официальный учебник Калейдоскоп но учебник JIT действительно сложен и, кажется, фокусируется на оптимизации и лени, хотя я все еще не могу понять, как легко скомпилировать модуль и вызвать из него функцию.
Любая помощь?
Итак, я просмотрел KaleidoscopeJIT и нашел самые важные части. Прежде всего обратите внимание, что я использую LLVM-4,0. У меня было много проблем, потому что я не осознавал, насколько действительно несовместимы версии 4.0 и ниже.
Код работает с C ++ 11. я использую лязг ++ — 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;
}
Я не уверен, что все включения необходимы, но в любом случае это работает как шарм. Хотя я с нетерпением жду каких-либо комментариев о том, как его улучшить. 🙂
Используя предоставленные 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à.
}