分享

用Visual Studio实现一个最小的LLVM JIT程序

 quasiceo 2018-01-16


因工作需要,最近两天刚开始看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实现。

  1. #include <llvm/ExecutionEngine/ExecutionEngine.h>  
  2. #include <llvm/IR/Module.h>  
  3. #include <llvm/IR/Constants.h>  
  4. #include <llvm/IR/Function.h>  
  5. #include <llvm/IR/DerivedTypes.h>  
  6. #include <llvm/IR/Type.h>  
  7. #include <llvm/IR/LLVMContext.h>  
  8. #include <llvm/IR/IRBuilder.h>  
  9. #include <llvm/Analysis/Verifier.h>  
  10. #include <llvm/Support/TargetSelect.h>  
  11. #include <llvm/ExecutionEngine/JIT.h>  
  12. #include <iostream>  
  13. #include <vector>  
  14.   
  15. int CalcSum(double nArg1, double nArg2)  
  16. {  
  17.     using namespace llvm;  
  18.     InitializeNativeTarget();  
  19.     LLVMContext &context = getGlobalContext();  
  20.     Module module("my module", context);  
  21.   
  22.     // Create the JIT.  This takes ownership of the module.  
  23.     std::string sError;  
  24.     ExecutionEngine *pExecutor =   
  25.         EngineBuilder(&module).setErrorStr(&sError).create();  
  26.     if (!pExecutor) {  
  27.         fprintf(stderr, "Creating ExecutionEngine error: %s\n", sError.c_str());  
  28.         exit(EINVAL);  
  29.     }  
  30.   
  31.     IRBuilder<> builder(context);  
  32.   
  33.     //Create a function type without params.  
  34.     std::vector<Type*> args;  
  35.     FunctionType *pFuncType = FunctionType::get(  
  36.         Type::getDoubleTy(context), args, false);  
  37.   
  38.     //Create a function with external linkage.  
  39.     Function *pFunc = Function::Create(  
  40.         pFuncType, Function::ExternalLinkage, "Foo", &module);  
  41.   
  42.     // Create the entry basic block.  
  43.     BasicBlock *pBlock = BasicBlock::Create(context, "entry", pFunc);  
  44.     builder.SetInsertPoint(pBlock);  
  45.   
  46.     //Generate the codes: nArg1 + nArg2.  
  47.     Value *pV1 = ConstantFP::get(context, APFloat(nArg1));  
  48.     Value *pV2 = ConstantFP::get(context, APFloat(nArg2));  
  49.     Value *pRetVal = builder.CreateFAdd(pV1, pV2, "addtmp");  
  50.     if (!pRetVal) {  
  51.         // Error reading body, remove function.  
  52.         pFunc->eraseFromParent();  
  53.         std::cerr << "Reading function body error.\n";  
  54.         return ENOENT;  
  55.     }  
  56.   
  57.     // Finish off the function.  
  58.     builder.CreateRet(pRetVal);  
  59.   
  60.     // Validate the generated code, checking for consistency.  
  61.     verifyFunction(*pFunc);  
  62.   
  63.     pFunc->dump();  
  64.   
  65.     // JIT the function, returning a function pointer.  
  66.     void *pJITFunc = pExecutor->getPointerToFunction(pFunc);  
  67.   
  68.     // Cast it to the right type (takes no arguments, returns a double) so we  
  69.     // can call it as a native function.  
  70.     double (*pf)() = (double (*)())(intptr_t)pJITFunc;  
  71.     fprintf(stderr, "Evaluated: %f + %f = %f\n", nArg1, nArg2, pf());  
  72.   
  73.     return 0;  
  74. }  
  75.   
  76. int main()  
  77. {  
  78.     return CalcSum(100, 200);  
  79. }  

四、注意事项

1. 一定要包含下面的头文件:

  1. #include <llvm/ExecutionEngine/JIT.h>  
否则会被连接器优化掉,运行时提示“Creating ExecutionEngine error: Interpreter has not been linked in.”

2. 官方用automake配置一个LLVM项目的例子,参见:

http:///docs/Projects.html

3. 上面的程序中:

  1. 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

版权声明:欢迎转载博主的原创文章,但请注明出处。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多