因工作需要,最近两天刚开始看LLVM。因为LLVM官方用了CMake,对于直接构建VC++工程介绍的比较少,所以我尝试从零开始,用VC写了一个最简单LLVM JIT的小例子,下面是具体步骤。
一、安装配置LLVM
下载并用VS编译安装LLVM,可以参考:
http://llvm.org/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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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 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
否则会被连接器优化掉,运行时提示“Creating ExecutionEngine error: Interpreter has not been linked in.”
2. 官方用automake配置一个LLVM项目的例子,参见:
http://llvm.org/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