• 2010-04-30

    Hello,LLVM

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/62862705.html

    LLVM是Low Level Virtual Machine的缩写,虽然名字叫了虚拟机,但它的目标可很宏伟,要做一套编译器的基础架构,从编译到运行无所不包。

    构建LLVM

    先来下载LLVM的发行版本
        http://llvm.org/releases/

    每个LLVM的发行版里都包括很多内容,比如为LLVM提供的C语言前端Clang,通向JVM和.NET的VMKit,给gcc提供的插件DragonEgg等等。这里,我们选择从LLVM源码开始。

    LLVM以CMake作为构建系统,以便在不同的平台上用不同的编译器都可以进行构建。为了更好的对构建内容进行统一,我们在LLVM的目录下建立一个build目录,这样,就可以把所有生成的内容都放到这个目录下。进入到这个目录下
        llvm/build$ cmake ..
    这个命令运行的结果会在build目录下生成一个Makefile。上面只是按照缺省配置进行生成,我们也可以对其定制,比如:
        llvm/build$ cmake .. -DCMAKE_INSTALL_PREFIX=/home/dreamhead/applilcations/llvm -DBUILD_SHARED_LIBS=ON
    通过CMAKE_INSTALL_PREFIX,我们指定了安装的路径,让BUILD_SHARED_LIBS等于ON,生成的就是动态库,而非静态库。

    有了Makefile,就可以进行真正的构建了。
        llvm/build$ make

    经过漫长的等待,最终会生成LLVM的库,然后就可以进行安装了
        llvm/build$ make install

    例子时间

    在下面这个例子里面,我们用LLVM的指令集创建了一个非常简单的函数,求和,它等价于:
      int add(int a, int b) {
        return a + b;
      }
    之后,我们以1和2作为参数调用了这个函数,并把运行结果显示出来。

    #include "llvm/LLVMContext.h"
    #include "llvm/Module.h"
    #include "llvm/Function.h"
    #include "llvm/Analysis/Verifier.h"
    #include "llvm/ExecutionEngine/GenericValue.h"
    #include "llvm/ExecutionEngine/JIT.h"
    #include "llvm/DerivedTypes.h"
    #include "llvm/Support/IRBuilder.h"
    #include "llvm/Support/raw_ostream.h"
    #include "llvm/Target/TargetSelect.h"

    using namespace llvm;
    using namespace std;

    Function* create_add_function(Module* module, LLVMContext& context) {
      // 函数基本信息
      Constant* c = module->getOrInsertFunction("add", // 函数名
        // 返回值
        IntegerType::get(context, 32),                                            
        // 函数参数
        IntegerType::get(context, 32), IntegerType::get(context, 32),
        NULL);
      Function* add = cast<Function> (c);
      // 调用约定
      add->setCallingConv(CallingConv::C);                           

      // 参数名
      Function::arg_iterator args = add->arg_begin();
      Value* a = args++;
      a->setName("a");
      Value* b = args++;
      b->setName("b");

      // 指令块
      BasicBlock* block = BasicBlock::Create(context, "entry", add);
      IRBuilder<> builder(block);
      // tmp = a + b
      Value* tmp = builder.CreateBinOp(Instruction::Add, a, b, "tmp");
      // return tmp
      builder.CreateRet(tmp);

      return add;
    }

    int main(int argc, char** argv) {
      // 创建Context
      LLVMContext& context = getGlobalContext();
      // 创建Module
      Module* module = new Module("test", context);
      // 基于Module和Context创建函数
      Function* add = create_add_function(module, context);

      // 校验Module
      verifyModule(*module, PrintMessageAction);

      // 输出Module的内容
      outs() << *module << "\n";

      // 准备运行函数
      InitializeNativeTarget();
      ExecutionEngine* engine = EngineBuilder(module).create();

      // 准备实参
      vector<GenericValue> args(2);
      args[0].IntVal = APInt(32, 1);
      args[1].IntVal = APInt(32, 2);

      // 调用函数
      GenericValue result = engine->runFunction(add, args);
      // 显示返回值
      outs() << "Result: " << result.IntVal << "\n";

      delete module;

      return 0;
    }

    接下来就是编译了,因为LLVM编译链接牵扯到很多细节,所以,它提供了一个llvm-config为我们生成这些编译选项。
      g++ -o llvm-add main.cpp `llvm-config --cxxflags --ldflags --libs jit nativecodegen`

    好,一切就绪,运行!
      ./llvm-add

    输出如下:
    ; ModuleID = 'test'

    define i32 @add(i32 %a, i32 %b) {
    entry:
      %tmp = add i32 %a, %b                           ; <i32> [#uses=1]
      ret i32 %tmp
    }

    Result: 3

    分享到:

    历史上的今天:

    引用地址:

    评论

  • 博主你好,请问你提供的例子是作什么用的。我是说能够用来干什么。LLVM作为编译器,可以编译源程序,生成LLVM的中间表示,然后通过lli解释执行。但你的例子只是使用LLVM的若干头文件,加上一些llvm-config的参数,不是很明白:)
    回复erlv说:
    这个例子是用来展示生成中间代码,等价于JVM平台上的bytecode。对应到编译器的实现上,可以用作编译器后端生成代码部分的实现。
    2010-05-04 19:52:00