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

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

你可能感兴趣的:(C/C++编程,编译技术)