因工作需要,最近两天刚开始看LLVM。因为LLVM官方用了CMake,对于直接构建VC++工程介绍的比较少,所以我尝试从零开始,用VC写了一个最简单LLVM JIT的小例子,下面是具体步骤。
一、安装配置LLVM
下载并用VS编译安装LLVM,可以参考:
http:///docs/GettingStartedVS.html
二、创建和配置我们的项目
1. 用VS新建一个空的C++项目,添加一个main.cpp文件,用于写下面的代码。
2. 在项目属性中,添加相应的LLVM目录,假设$LLVM为安装目录。包含目录:$LLVM\include
库目录:$LLVM\lib。
3. 在项目属性中添加C++预处理宏:
_SCL_SECURE_NO_WARNINGS
_CRT_SECURE_NO_WARNINGS
4. 在链接属性中添加下列以下库:
LLVMJIT.lib
LLVMX86CodeGen.lib
LLVMExecutionEngine.lib
LLVMAsmPrinter.lib
LLVMSelectionDAG.lib
LLVMX86Desc.lib
LLVMMCParser.lib
LLVMCodeGen.lib
LLVMX86AsmPrinter.lib
LLVMX86Info.lib
LLVMScalarOpts.lib
LLVMX86Utils.lib
LLVMInstCombine.lib
LLVMTransformUtils.lib
LLVMipa.lib
LLVMAnalysis.lib
LLVMTarget.lib
LLVMCore.lib
LLVMMC.lib
LLVMObject.lib
LLVMSupport.lib
JIT的配置依赖于CPU和操作系统,不同的平台需要包含的库文件有所差异。
5. 在VS中禁用警告:
4244;4800
下面的程序用LLVM IR指令实现两个浮点数加法,用JIT实现。
- #include <llvm/ExecutionEngine/ExecutionEngine.h>
- #include <llvm/IR/Module.h>
- #include <llvm/IR/Constants.h>
- #include <llvm/IR/Function.h>
- #include <llvm/IR/DerivedTypes.h>
- #include <llvm/IR/Type.h>
- #include <llvm/IR/LLVMContext.h>
- #include <llvm/IR/IRBuilder.h>
- #include <llvm/Analysis/Verifier.h>
- #include <llvm/Support/TargetSelect.h>
- #include <llvm/ExecutionEngine/JIT.h>
- #include <iostream>
- #include <vector>
- int CalcSum(double nArg1, double nArg2)
- {
- using namespace llvm;
- InitializeNativeTarget();
- LLVMContext &context = getGlobalContext();
- Module module("my module", context);
- // Create the JIT. This takes ownership of the module.
- std::string sError;
- ExecutionEngine *pExecutor =
- EngineBuilder(&module).setErrorStr(&sError).create();
- if (!pExecutor) {
- fprintf(stderr, "Creating ExecutionEngine error: %s\n", sError.c_str());
- exit(EINVAL);
- }
- IRBuilder<> builder(context);
- //Create a function type without params.
- std::vector<Type*> args;
- FunctionType *pFuncType = FunctionType::get(
- Type::getDoubleTy(context), args, false);
- //Create a function with external linkage.
- Function *pFunc = Function::Create(
- pFuncType, Function::ExternalLinkage, "Foo", &module);
- // Create the entry basic block.
- BasicBlock *pBlock = BasicBlock::Create(context, "entry", pFunc);
- builder.SetInsertPoint(pBlock);
- //Generate the codes: nArg1 + nArg2.
- Value *pV1 = ConstantFP::get(context, APFloat(nArg1));
- Value *pV2 = ConstantFP::get(context, APFloat(nArg2));
- Value *pRetVal = builder.CreateFAdd(pV1, pV2, "addtmp");
- if (!pRetVal) {
- // Error reading body, remove function.
- pFunc->eraseFromParent();
- std::cerr << "Reading function body error.\n";
- return ENOENT;
- }
- // Finish off the function.
- builder.CreateRet(pRetVal);
- // Validate the generated code, checking for consistency.
- verifyFunction(*pFunc);
- pFunc->dump();
- // JIT the function, returning a function pointer.
- void *pJITFunc = pExecutor->getPointerToFunction(pFunc);
- // Cast it to the right type (takes no arguments, returns a double) so we
- // can call it as a native function.
- double (*pf)() = (double (*)())(intptr_t)pJITFunc;
- fprintf(stderr, "Evaluated: %f + %f = %f\n", nArg1, nArg2, pf());
- return 0;
- }
- int main()
- {
- return CalcSum(100, 200);
- }
四、注意事项
1. 一定要包含下面的头文件:
- #include <llvm/ExecutionEngine/JIT.h>
2. 官方用automake配置一个LLVM项目的例子,参见:
http:///docs/Projects.html
3. 上面的程序中:
- pFunc->dump();
表示输出LLVM IR指令。输出结果如下:
===================================
define double @Foo() {
entry:
ret double 3.000000e+02
}
Evaluated: 100.000000 + 200.000000 = 300.000000
===================================
最后一行的结果是JIT指令运行得到的。
注意,缺省情况下,LLVM对于输出的中间指令已经进行了常量折叠。
4. VC工程的附加编译和链接选项设置,也可以用llvm-config命令生成:
llvm-config --cppflags --ldflags --libs core support