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 %0, 10
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 %1, 2
%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 %6, 1
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 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'
注意:
类llvm::LLVMContext
不是线程安全的。在多线程环境下,不同的线程应该各自持有一份llvm::LLVMContext
类的实例。
一个llvm::Module
类的对象在进行任何操作之前都必须确保其构造函数中所传入的llvm::LLVMContext
对象是有效的(引用传递)。
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
)。
注:
构造向量FuncTyAgrs
时,其初始元素数量应该等于要创建函数的参数个数。从而,避免不必要的性能开销。
函数Type::getXXXTy()
用于获取表示数据类型XXX
的对象。
函数FunctionType::get()
的第三个参数表示是否带可变参数。如果值为false
,表示该函数没有可变参数...
。
函数Function::Createt()
的第二个参数用于指定函数的链接属性。如果值为Function::ExternalLinkage
,表示该函数是全局函数。
需要注意的是, 由于 C++ 程序会进行名称重编。因此,函数foo
的真正名称为重编后的名称(这里是_Z3fooi
)。
step 3: 设置运行时抢占符
设置运行时抢占符
的代码如下:
FuncFoo->setDSOLocal(true);
上述代码的逻辑为:将FuncFoo
所表示函数的运行时抢占符设置为dso_local
。
step 4: 设置函数属性
设置函数属性(数值形式)
的代码如下:
FuncFoo->addFnAttr(Attribute::NoInline);
FuncFoo->addFnAttr(Attribute::NoUnwind);
上述代码的逻辑为:为FuncFoo
所表示的函数添加属性:noinline
、nounwind
。
注:这些属性定义在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(32, 10)));
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();
注:
语句Out->keep();
,用于指示不要删除生成的位码文件。
类ToolOutputFile
构造函数中的第二个参数用于指定要生成的位码文件的名称及路径(这里是./test.bc
,即生成的位码文件保存到当前目录下的test.bc
文件中)。
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(32, 0)), 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(32, 10)));
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(32, 2)), "rem", ForBodyBB);
142 auto *Cmp1 = new ICmpInst(*ForBodyBB, ICmpInst::ICMP_EQ,
143 Rem, ConstantInt::get(Ctx, APInt(32, 0)));
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(32, 1)), "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(32, 4))),
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 %0, 10
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 %1, 2
%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 %6, 1
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 汇编文件。
下一篇:LLVM 之 IR 篇(3):如何使用 LLVM IR 优化器