LLVM 之 IR 篇(2):如何编写生成 LLVM IR 的工具

Author: stormQ

Created: Thursday, 22. July 2021 11:54PM

Last Modified: Wednesday, 28. July 2021 08:33PM



摘要

本文基于release/12.x版本的 LLVM 源码,通过示例展示了如何利用 LLVM API 编写一个简单的生成 LLVM IR 的独立程序。从而,初步了解 LLVM IR 的内存表示形式以便更深入地研究相关内容。


准备示例

本节介绍了所研究示例的源码以及通过相关命令生成的 LLVM IR 汇编文件。

step 1: 编写示例程序

test.cpp:

void foo(int x) {
  for (int i = 0; i < 10; i++) {
    if (x % 2 == 0) {
      x += i;
    }
    else {
      x -= i;
    }
  }
}

step 2: 生成 LLVM IR 汇编文件

$ clang test.cpp -emit-llvm -c -o test.bc
$ clang test.bc -emit-llvm -S -c -o test.ll

注:如果通过执行clang test.cpp -emit-llvm -S -c -o test.ll生成 LLVM IR 汇编文件,那么ModuleID的值将为test.cpp

生成的汇编文件——test.ll 的内容如下(部分):

; ModuleID = 'test.bc'
source_filename = "test.cpp"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: noinline nounwind optnone uwtable mustprogress
define dso_local void @_Z3fooi(i32 %x) #0 {
entry:
  %x.addr = alloca i32, align 4
  %i = alloca i32, align 4
  store i32 %x, i32* %x.addr, align 4
  store i32 0, i32* %i, align 4
  br label %for.cond

for.cond:                                         ; preds = %for.inc, %entry
  %0 = load i32, i32* %i, align 4
  %cmp = icmp slt i32 %010
  br i1 %cmp, label %for.body, label %for.end

for.body:                                         ; preds = %for.cond
  %1 = load i32, i32* %x.addr, align 4
  %rem = srem i32 %12
  %cmp1 = icmp eq i32 %rem, 0
  br i1 %cmp1, label %if.then, label %if.else

if.then:                                          ; preds = %for.body
  %2 = load i32, i32* %i, align 4
  %3 = load i32, i32* %x.addr, align 4
  %add = add nsw i32 %3, %2
  store i32 %add, i32* %x.addr, align 4
  br label %if.end

if.else:                                          ; preds = %for.body
  %4 = load i32, i32* %i, align 4
  %5 = load i32, i32* %x.addr, align 4
  %sub = sub nsw i32 %5, %4
  store i32 %sub, i32* %x.addr, align 4
  br label %if.end

if.end:                                           ; preds = %if.else, %if.then
  br label %for.inc

for.inc:                                          ; preds = %if.end
  %6 = load i32, i32* %i, align 4
  %inc = add nsw i32 %61
  store i32 %inc, i32* %i, align 4
  br label %for.cond, !llvm.loop !2

for.end:                                          ; preds = %for.cond
  ret void
}

attributes #0 = { noinline nounwind optnone uwtable mustprogress "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 12.0.0 (...)"}
!2 = distinct !{!2, !3}
!3 = !{!"llvm.loop.mustprogress"}

注: LLVM IR 汇编文件中的行注释以英文分号;开头,并且仅支持行注释(即无块注释)。


编写生成 LLVM IR 的工具

本节介绍了如何利用 LLVM API 编写生成上一节中 LLVM IR 汇编文件的程序,包括:创建模块创建函数设置运行时抢占符设置函数属性设置函数参数名称创建基本块分配栈内存内存写入无条件跳转比较大小有条件跳转二元运算(加法)函数返回 等。

step 1: 创建模块

模块(Module)是 LLVM IR 中最顶层的实体,包含了所有其他 IR 对象(比如:函数、基本块等)。LLVM IR 程序是由模块组成的,每个模块对应输入程序的一个编译单元。

创建模块的代码如下:

LLVMContext Ctx;
std::unique_ptr<Module> M(new Module("test.cpp", Ctx));

上述代码的逻辑为:创建一个模块 ID 为"test.cpp"的模块对象M。该对象的生命周期通过智能指针std::unique_ptr进行管理。

LLVM IR 汇编文件首行的注释中包含了模块 ID,示例:

; ModuleID = 'test.cpp'

注意:

step 2: 创建函数

创建函数的代码如下:

SmallVector<Type *, 1> FuncTyAgrs;
FuncTyAgrs.push_back(Type::getInt32Ty(Ctx));
auto *FuncTy = FunctionType::get(Type::getVoidTy(Ctx), FuncTyAgrs, false);
auto *FuncFoo =
    Function::Create(FuncTy, Function::ExternalLinkage, "_Z3fooi", M.get());

上述代码的逻辑为:创建一个函数原型为void _Z3fooi(int)的全局函数,由类llvm::FunctionType的对象表示(这里是FuncFoo)。

注:

需要注意的是, 由于 C++ 程序会进行名称重编。因此,函数foo的真正名称为重编后的名称(这里是_Z3fooi)。

step 3: 设置运行时抢占符

设置运行时抢占符的代码如下:

FuncFoo->setDSOLocal(true);

上述代码的逻辑为:将FuncFoo所表示函数的运行时抢占符设置为dso_local

step 4: 设置函数属性

设置函数属性(数值形式)的代码如下:

FuncFoo->addFnAttr(Attribute::NoInline);
FuncFoo->addFnAttr(Attribute::NoUnwind);

上述代码的逻辑为:为FuncFoo所表示的函数添加属性:noinlinenounwind

注:这些属性定义在llvm/IR/Attributes.inc文件中。该文件是构建 Clang 时生成的,位于构建目录的子目录include中,比如:build_ninja/include/llvm/IR/Attributes.inc

设置函数属性(字符串形式)的代码如下:

AttrBuilder FuncAttrs;
FuncAttrs.addAttribute("disable-tail-calls", llvm::toStringRef(false));
FuncAttrs.addAttribute("frame-pointer""all");
FuncFoo->addAttributes(AttributeList::FunctionIndex, FuncAttrs);

上述代码的逻辑为:为FuncFoo所表示的函数添加属性:"disable-tail-calls"="false""frame-pointer"="all"

step 5: 设置函数参数名称

设置函数参数名称的代码如下:

Function::arg_iterator Args = FuncFoo->arg_begin();
Args->setName("x");

上述代码的逻辑为:将FuncFoo所表示函数的(从左到右)第一个参数的名称设置为x

step 6: 创建基本块

创建基本块的代码如下:

BasicBlock *EntryBB = BasicBlock::Create(Ctx, "entry", FuncFoo, nullptr);

上述代码的逻辑为:在FuncFoo所表示的函数中创建一个标签为entry的基本块,由类llvm::BasicBlock的对象表示(这里是EntryBB),并且无前继节点。

注:函数BasicBlock::Create()的第四个参数用于指定基本块的前继节点。如果值为nullptr,表示要创建的基本块无前继节点。

step 7: 分配栈内存

分配栈内存的代码如下:

auto *AIX = new AllocaInst(Type::getInt32Ty(Ctx), 0"x.addr", EntryBB);
AIX->setAlignment(Align(4));

上述代码的逻辑为:在基本块EntryBB的末尾添加一条alloca指令,并且以 4 字节进行对齐。

注:AllocaInst构造函数中的第二个参数用于指定地址空间。如果值为 0,表示默认的地址空间。

对应的 LLVM IR 如下:

%x.addr = alloca i32, align 4

上述内容表示,局部标识符%x.addr指向一块大小为 4 字节的栈内存,并且以 4 字节进行对齐。

step 8: 内存写入

内存写入的代码如下:

auto *StX = new StoreInst(X, AIX, false, EntryBB);
StX->setAlignment(Align(4));

上述代码的逻辑为:在基本块EntryBB的末尾添加一条store指令,并且以 4 字节进行对齐。

注:StoreInst构造函数中的第三个参数表示是否指定volatile属性。如果值为false,表示不指定。

对应的 LLVM IR 如下:

store i32 %x, i32* %x.addr, align 4

上述内容表示,将局部标识符%x的值写入到局部标识符%x.addr所指向的内存中,并且以 4 字节进行对齐。

step 9: 无条件跳转

无条件跳转的代码如下:

BranchInst::Create(ForCondBB, EntryBB);

上述代码的逻辑为:在基本块EntryBB的末尾添加一条br指令(无条件跳转)。即直接从基本块EntryBB跳转到另一个基本块ForCondBB

对应的 LLVM IR 如下:

br label %for.cond

上述内容表示,直接跳转到标签为for.cond的基本块。

step 10: 比较大小

比较大小的代码如下:

auto *Cmp = new ICmpInst(*ForCondBB, ICmpInst::ICMP_SLT,
    Ld0, ConstantInt::get(Ctx, APInt(3210)));
Cmp->setName("cmp");

上述代码的逻辑为:在基本块ForCondBB的末尾添加一条icmp指令,比较结果保存到局部变量Cmp中。

对应的 LLVM IR 如下:

%cmp = icmp slt i32 %0, 10

上述内容表示,判断局部标志符%0的值是否小于 10。如果是,那么局部标识符%cmp的值为true

step 11: 有条件跳转

有条件跳转的代码如下:

BranchInst::Create(ForBodyBB, ForEndBB, Cmp, ForCondBB);

上述代码的逻辑为:在基本块ForCondBB的末尾添加一条br指令(有条件跳转)。即如果局部变量Cmp所表示的值为true,则跳转到基本块ForBodyBB;否则,跳转到基本块ForEndBB

对应的 LLVM IR 如下:

br i1 %cmp, label %for.body, label %for.end

上述内容表示,如果局部标识符%cmp的值为true,则跳转到标签为for.body的基本块;否则,跳转到标签为for.end的基本块。

step 12: 二元运算(加法)

加法运算的代码如下:

auto *Add = BinaryOperator::Create(Instruction::Add, Ld3, Ld2, "add", IfThenBB);
Add->setHasNoSignedWrap();

上述代码的逻辑为:在基本块IfThenBB的末尾添加一条add指令,并且加法运算的属性设置为nsw

对应的 LLVM IR 如下:

%add = add nsw i32 %3, %2

上述内容表示,将局部标志符%3%2的值相加,结果保存到局部标志符%add中。

step 13: 函数返回

函数返回的代码如下:

ReturnInst::Create(Ctx, nullptr, ForEndBB);

上述代码的逻辑为:在基本块ForEndBB的末尾添加一条ret指令(无返回值)。

注:函数ReturnInst::Create()的第二个参数表示函数的返回值类型。如果值为nullptr,表示返回值的类型为void

对应的 LLVM IR 如下:

ret void

上述内容表示,函数返回值的类型为void

step 14: 检查 IR 是否合法

if (verifyModule(*M)) {
  errs() << "Error: module failed verification. This shouldn't happen.\n";
  exit(1);
}

注:函数verifyModule()用于检查 IR 是否合法。比如:一个基本块必须以teminator instruction结尾,并且只能有一个。

step 15: 生成位码文件

std::error_code EC;
std::unique_ptr<ToolOutputFile> Out(
    new ToolOutputFile("./test.bc", EC, sys::fs::F_None));
if (EC) {
  errs() << EC.message() << '\n';
  exit(2);
}
WriteBitcodeToFile(*M, Out->os());
Out->keep();

注:

step 16: 完整的程序

main_ir.cpp:

  1 #include "llvm/IR/Module.h"
  2 #include "llvm/ADT/SmallVector.h"
  3 #include "llvm/IR/Function.h"
  4 #include "llvm/IR/Mangler.h"
  5 #include "llvm/IR/Instructions.h"
  6 #include "llvm/Support/raw_ostream.h"
  7 #include "llvm/IR/Verifier.h"
  8 #include "llvm/Support/ToolOutputFile.h"
  9 #include "llvm/Support/FileSystem.h"
 10 #include "llvm/Bitcode/BitcodeWriter.h"
 11 #include <system_error>
 12 #include <map>
 13 #include <string>
 14 
 15 using namespace llvm;
 16 
 17 static MDNode *getID(LLVMContext &Ctx, Metadata *arg0 = nullptr,
 18                      Metadata *arg1 = nullptr) {
 19   MDNode *ID;
 20   SmallVector<Metadata *, 3> Args;
 21   // Reserve operand 0 for loop id self reference.
 22   Args.push_back(nullptr);
 23 
 24   if (arg0)
 25     Args.push_back(arg0);
 26   if (arg1)
 27     Args.push_back(arg1);
 28 
 29   ID = MDNode::getDistinct(Ctx, Args);
 30   ID->replaceOperandWith(0, ID);
 31   return ID;
 32 }
 33 
 34 // define dso_local void @_Z3fooi(i32 %x) #0 {...}
 35 static Function *createFuncFoo(Module *M) {
 36   assert(M);
 37 
 38   SmallVector<Type *, 1> FuncTyAgrs;
 39   FuncTyAgrs.push_back(Type::getInt32Ty(M->getContext()));
 40   auto *FuncTy =
 41       FunctionType::get(Type::getVoidTy(M->getContext()), FuncTyAgrs, false);
 42 
 43   auto *FuncFoo =
 44     Function::Create(FuncTy, Function::ExternalLinkage, "_Z3fooi", M);
 45 
 46   Function::arg_iterator Args = FuncFoo->arg_begin();
 47   Args->setName("x");
 48 
 49   return FuncFoo;
 50 }
 51 
 52 static void setFuncAttrs(Function *FuncFoo{
 53   assert(FuncFoo);
 54 
 55   static constexpr Attribute::AttrKind FuncAttrs[] = {
 56       Attribute::NoInline, Attribute::NoUnwind, Attribute::OptimizeNone,
 57       Attribute::UWTable, Attribute::MustProgress,
 58   };
 59 
 60   for (auto Attr : FuncAttrs) {
 61     FuncFoo->addFnAttr(Attr);
 62   }
 63 
 64   static std::map<std::string, std::string> FuncAttrsStr = {
 65       {"disable-tail-calls""false"}, {"frame-pointer""all"},
 66       {"less-precise-fpmad""false"}, {"min-legal-vector-width""0"},
 67       {"no-infs-fp-math""false"}, {"no-jump-tables""false" },
 68       {"no-nans-fp-math""false"}, {"no-signed-zeros-fp-math""false"},
 69       {"no-trapping-math""true"}, {"stack-protector-buffer-size""8"},
 70       {"target-cpu""x86-64"},
 71       {"target-features""+cx8,+fxsr,+mmx,+sse,+sse2,+x87"},
 72       {"tune-cpu""generic"}, {"unsafe-fp-math""false"},
 73       {"use-soft-float""false"},
 74   };
 75 
 76   AttrBuilder Builder;
 77   for (auto Attr : FuncAttrsStr) {
 78     Builder.addAttribute(Attr.first, Attr.second);
 79   }
 80   FuncFoo->addAttributes(AttributeList::FunctionIndex, Builder);
 81 }
 82 
 83 int main() {
 84   LLVMContext Ctx;
 85   std::unique_ptr<Module> M(new Module("test.cpp", Ctx));
 86 
 87   M->setSourceFileName("test.cpp");
 88   M->setDataLayout(
 89     "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128");
 90   M->setTargetTriple("x86_64-unknown-linux-gnu");
 91 
 92   auto *FuncFoo = createFuncFoo(M.get());
 93   FuncFoo->setDSOLocal(true);
 94   setFuncAttrs(FuncFoo);
 95 
 96   BasicBlock *EntryBB = BasicBlock::Create(Ctx, "entry", FuncFoo, nullptr);
 97   BasicBlock *ForCondBB = BasicBlock::Create(Ctx, "for.cond", FuncFoo, nullptr);
 98 
 99   // entry:
100   //   %x.addr = alloca i32, align 4
101   //   %i = alloca i32, align 4
102   //   store i32 %x, i32* %x.addr, align 4
103   //   store i32 0, i32* %i, align 4
104   //   br label %for.cond
105   auto *AIX = new AllocaInst(Type::getInt32Ty(Ctx), 0"x.addr", EntryBB);
106   AIX->setAlignment(Align(4));
107   auto *AII = new AllocaInst(Type::getInt32Ty(Ctx), 0"i", EntryBB);
108   AII->setAlignment(Align(4));
109   auto *StX = new StoreInst(FuncFoo->arg_begin(), AIX, false, EntryBB);
110   StX->setAlignment(Align(4));
111   auto *StI =
112       new StoreInst(ConstantInt::get(Ctx, APInt(320)), AII, false, EntryBB);
113   StI->setAlignment(Align(4));
114   BranchInst::Create(ForCondBB, EntryBB);
115 
116   BasicBlock *ForBodyBB = BasicBlock::Create(Ctx, "for.body", FuncFoo, nullptr);
117   BasicBlock *ForEndBB = BasicBlock::Create(Ctx, "for.end", FuncFoo, nullptr);
118 
119   // for.cond:                                         ; preds = %for.inc, %entry
120   //   %0 = load i32, i32* %i, align 4
121   //   %cmp = icmp slt i32 %0, 10
122   //   br i1 %cmp, label %for.body, label %for.end
123   auto *Ld0 = new LoadInst(Type::getInt32Ty(Ctx), AII, ""false, ForCondBB);
124   Ld0->setAlignment(Align(4));
125   auto *Cmp = new ICmpInst(*ForCondBB, ICmpInst::ICMP_SLT,
126       Ld0, ConstantInt::get(Ctx, APInt(3210)));
127   Cmp->setName("cmp");
128   BranchInst::Create(ForBodyBB, ForEndBB, Cmp, ForCondBB);
129 
130   BasicBlock *IfThenBB = BasicBlock::Create(Ctx, "if.then", FuncFoo, nullptr);
131   BasicBlock *IfElseBB = BasicBlock::Create(Ctx, "if.else", FuncFoo, nullptr);
132 
133   // for.body:                                         ; preds = %for.cond
134   //   %1 = load i32, i32* %x.addr, align 4
135   //   %rem = srem i32 %1, 2
136   //   %cmp1 = icmp eq i32 %rem, 0
137   //   br i1 %cmp1, label %if.then, label %if.else
138   auto *Ld1 = new LoadInst(Type::getInt32Ty(Ctx), AIX, ""false, ForBodyBB);
139   Ld1->setAlignment(Align(4));
140   auto *Rem = BinaryOperator::Create(Instruction::SRem, Ld1,
141       ConstantInt::get(Ctx, APInt(322)), "rem", ForBodyBB);
142   auto *Cmp1 = new ICmpInst(*ForBodyBB, ICmpInst::ICMP_EQ,
143       Rem, ConstantInt::get(Ctx, APInt(320)));
144   Cmp1->setName("cmp1");
145   BranchInst::Create(IfThenBB, IfElseBB, Cmp1, ForBodyBB);
146 
147   BasicBlock *IfEndBB = BasicBlock::Create(Ctx, "if.end", FuncFoo, nullptr);
148 
149   // if.then:                                          ; preds = %for.body
150   //   %2 = load i32, i32* %i, align 4
151   //   %3 = load i32, i32* %x.addr, align 4
152   //   %add = add nsw i32 %3, %2
153   //   store i32 %add, i32* %x.addr, align 4
154   //   br label %if.end
155   auto *Ld2 = new LoadInst(Type::getInt32Ty(Ctx), AII, ""false, IfThenBB);
156   Ld2->setAlignment(Align(4));
157   auto *Ld3 = new LoadInst(Type::getInt32Ty(Ctx), AIX, ""false, IfThenBB);
158   Ld3->setAlignment(Align(4));
159   auto *Add = BinaryOperator::Create(Instruction::Add, Ld3, Ld2, "add", IfThenBB);
160   Add->setHasNoSignedWrap();
161   auto *StAdd = new StoreInst(Add, AIX, false, IfThenBB);
162   StAdd->setAlignment(Align(4));
163   BranchInst::Create(IfEndBB, IfThenBB);
164 
165   // if.else:                                          ; preds = %for.body
166   //   %4 = load i32, i32* %i, align 4
167   //   %5 = load i32, i32* %x.addr, align 4
168   //   %sub = sub nsw i32 %5, %4
169   //   store i32 %sub, i32* %x.addr, align 4
170   //   br label %if.end
171   auto *Ld4 = new LoadInst(Type::getInt32Ty(Ctx), AII, ""false, IfElseBB);
172   Ld4->setAlignment(Align(4));
173   auto *Ld5 = new LoadInst(Type::getInt32Ty(Ctx), AIX, ""false, IfElseBB);
174   Ld5->setAlignment(Align(4));
175   auto *Sub = BinaryOperator::Create(Instruction::Sub, Ld5, Ld4, "sub", IfElseBB);
176   Sub->setHasNoSignedWrap();
177   auto *StSub = new StoreInst(Sub, AIX, false, IfElseBB);
178   StSub->setAlignment(Align(4));
179   BranchInst::Create(IfEndBB, IfElseBB);
180 
181   BasicBlock *ForIncBB = BasicBlock::Create(Ctx, "for.inc", FuncFoo, nullptr);
182 
183   // if.end:                                           ; preds = %if.else, %if.then
184   //   br label %for.inc
185   BranchInst::Create(ForIncBB, IfEndBB);
186 
187   // for.inc:                                          ; preds = %if.end
188   //   %6 = load i32, i32* %i, align 4
189   //   %inc = add nsw i32 %6, 1
190   //   store i32 %inc, i32* %i, align 4
191   //   br label %for.cond, !llvm.loop !2
192   auto *Ld6 = new LoadInst(Type::getInt32Ty(Ctx), AII, ""false, ForIncBB);
193   Ld6->setAlignment(Align(4));
194   auto *Inc = BinaryOperator::Create(Instruction::Add, Ld6,
195       ConstantInt::get(Ctx, APInt(321)), "inc", ForIncBB);
196   Inc->setHasNoSignedWrap();
197   auto *StInc = new StoreInst(Inc, AII, false, ForIncBB);
198   StInc->setAlignment(Align(4));
199   auto *BI = BranchInst::Create(ForCondBB, ForIncBB);
200   {
201     SmallVector<Metadata *, 1> Args;
202     Args.push_back(MDString::get(Ctx, "llvm.loop.mustprogress"));
203     auto *MData =
204         MDNode::concatenate(nullptr, getID(Ctx, MDNode::get(Ctx, Args)));
205     BI->setMetadata("llvm.loop", MData);
206   }
207 
208   // for.end:                                          ; preds = %for.cond
209   //   ret void
210   ReturnInst::Create(Ctx, nullptr, ForEndBB);
211 
212   // !llvm.module.flags = !{!0}
213   // !0 = !{i32 1, !"wchar_size", i32 4}
214   {
215     llvm::NamedMDNode *IdentMetadata = M->getOrInsertModuleFlagsMetadata();
216     Type *Int32Ty = Type::getInt32Ty(Ctx);
217     Metadata *Ops[3] = {
218         ConstantAsMetadata::get(ConstantInt::get(Int32Ty, Module::Error)),
219         MDString::get(Ctx, "wchar_size"),
220         ConstantAsMetadata::get(ConstantInt::get(Ctx, APInt(324))),
221     };
222     IdentMetadata->addOperand(MDNode::get(Ctx, Ops));
223   }
224 
225   // !llvm.ident = !{!1}
226   // !1 = !{!"clang version 12.0.0 (ubuntu1)"}
227   {
228     llvm::NamedMDNode *IdentMetadata = M->getOrInsertNamedMetadata("llvm.ident");
229     std::string ClangVersion("clang version 12.0.0 (...)");
230     llvm::Metadata *IdentNode[] = { llvm::MDString::get(Ctx, ClangVersion) };
231     IdentMetadata->addOperand(llvm::MDNode::get(Ctx, IdentNode));
232   }
233 
234   if (verifyModule(*M)) {
235     errs() << "Error: module failed verification. This shouldn't happen.\n";
236     exit(1);
237   }
238 
239   std::error_code EC;
240   std::unique_ptr<ToolOutputFile> Out(
241       new ToolOutputFile("./test.bc", EC, sys::fs::F_None));
242   if (EC) {
243     errs() << EC.message() << '\n';
244     exit(2);
245   }
246   WriteBitcodeToFile(*M, Out->os());
247   Out->keep();
248 
249   return 0;
250 }

构建并运行所编写的工具

step 1: 编译

方式 1:

$ g++ -o main_ir main_ir.cpp -g -lLLVMCore -lLLVMSupport -lLLVMBitWriter

step 2: 运行

1) 运行

$ ./main_ir 

2) 生成 LLVM IR 汇编文件

$ llvm-dis test.bc -o test.ll

通过自己编写的工具生成的 test.ll 的内容如下:

; ModuleID = 'test.bc'
source_filename = "test.cpp"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

; Function Attrs: noinline nounwind optnone uwtable mustprogress
define dso_local void @_Z3fooi(i32 %x) #0 {
entry:
  %x.addr = alloca i32, align 4
  %i = alloca i32, align 4
  store i32 %x, i32* %x.addr, align 4
  store i32 0, i32* %i, align 4
  br label %for.cond

for.cond:                                         ; preds = %for.inc, %entry
  %0 = load i32, i32* %i, align 4
  %cmp = icmp slt i32 %010
  br i1 %cmp, label %for.body, label %for.end

for.body:                                         ; preds = %for.cond
  %1 = load i32, i32* %x.addr, align 4
  %rem = srem i32 %12
  %cmp1 = icmp eq i32 %rem, 0
  br i1 %cmp1, label %if.then, label %if.else

for.end:                                          ; preds = %for.cond
  ret void

if.then:                                          ; preds = %for.body
  %2 = load i32, i32* %i, align 4
  %3 = load i32, i32* %x.addr, align 4
  %add = add nsw i32 %3, %2
  store i32 %add, i32* %x.addr, align 4
  br label %if.end

if.else:                                          ; preds = %for.body
  %4 = load i32, i32* %i, align 4
  %5 = load i32, i32* %x.addr, align 4
  %sub = sub nsw i32 %5, %4
  store i32 %sub, i32* %x.addr, align 4
  br label %if.end

if.end:                                           ; preds = %if.else, %if.then
  br label %for.inc

for.inc:                                          ; preds = %if.end
  %6 = load i32, i32* %i, align 4
  %inc = add nsw i32 %61
  store i32 %inc, i32* %i, align 4
  br label %for.cond, !llvm.loop !2
}

attributes #0 = { noinline nounwind optnone uwtable mustprogress "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 12.0.0 (...)"}
!2 = distinct !{!2, !3}
!3 = !{!"llvm.loop.mustprogress"}

从上面的结果可以看出,我们所编写的工具生成了几乎完全相同的 LLVM IR 汇编文件。


References


下一篇:LLVM 之 IR 篇(3):如何使用 LLVM IR 优化器

上一篇:LLVM 之 IR 篇(1):零基础快速入门 LLVM IR

首页