系列文章目录
LLVM系列第一章:编译LLVM源码
LLVM系列第二章:模块Module
LLVM系列第三章:函数Function
LLVM系列第四章:逻辑代码块Block
LLVM系列第五章:全局变量Global Variable
LLVM系列第六章:函数返回值Return
LLVM系列第七章:函数参数Function Arguments
LLVM系列第八章:算术运算语句Arithmetic Statement
LLVM系列第九章:控制流语句if-else
LLVM系列第十章:控制流语句if-else-phi
LLVM系列第十一章:写一个Hello World
LLVM系列第十二章:写一个简单的词法分析器Lexer
LLVM系列第十三章:写一个简单的语法分析器Parser
LLVM系列第十四章:写一个简单的语义分析器Semantic Analyzer
LLVM系列第十五章:写一个简单的中间代码生成器IR Generator
LLVM系列第十六章:写一个简单的编译器
LLVM系列第十七章:for循环
LLVM系列第十八章:写一个简单的IR处理流程Pass
LLVM系列第十九章:写一个简单的Module Pass
LLVM系列第二十章:写一个简单的Function Pass
LLVM系列第二十一章:写一个简单的Loop Pass
LLVM系列第二十二章:写一个简单的编译时函数调用统计器(Pass)
LLVM系列第二十三章:写一个简单的运行时函数调用统计器(Pass)
LLVM系列第二十四章:用Xcode编译调试LLVM源码
LLVM系列第二十五章:简单统计一下LLVM源码行数
LLVM系列第二十六章:理解LLVMContext
LLVM系列第二十七章:理解IRBuilder
LLVM系列第二十八章:写一个JIT Hello World
LLVM系列第二十九章:写一个简单的常量加法“消除”工具(Pass)
flex&bison系列
在此,记录下对LLVM源码中IRBuilder这个类的理解,以备查阅。
我们先来看看IRBuilder是怎么用的(示例):
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
using namespace llvm;
int main(int argc, char* argv[])
{
LLVMContext context;
IRBuilder<> builder(context);
// Create a module
Module* module = new Module("HelloModule", context);
// Add a function
Type* int32Type = builder.getInt32Ty();
FunctionType* functionType = FunctionType::get(int32Type, false);
Function* function = Function::Create(functionType, GlobalValue::ExternalLinkage, "HelloFunction", module);
// Create a block
BasicBlock* block = BasicBlock::Create(context, "entry", function);
builder.SetInsertPoint(block);
// Add a return
ConstantInt* zero = builder.getInt32(0);
builder.CreateRet(zero);
// Print the IR
module->print(outs(), nullptr);
return 0;
}
其生成的IR代码如下(示例):
; ModuleID = 'HelloModule'
source_filename = "HelloModule"
define i32 @HelloFunction() {
entry:
ret i32 0
}
从以上例子可以看出来,使用IRBuilder之前需要初始化(示例):
IRBuilder<> builder(context);
注意到它是个模板类,这是具体定义(示例):
template <typename FolderTy = ConstantFolder,
typename InserterTy = IRBuilderDefaultInserter>
class IRBuilder : public IRBuilderBase {
...
};
我们用它来获取了一个整数类型(示例):
Type* int32Type = builder.getInt32Ty();
我们还用它来创建了一个return指令(示例):
// Create a block
BasicBlock* block = BasicBlock::Create(context, "entry", function);
builder.SetInsertPoint(block);
// Add a return
ConstantInt* zero = builder.getInt32(0);
builder.CreateRet(zero);
具体来说,以上代码创建了一个代码块(Basic Block),并向代码块中插入了一个return语句,它返回了整数0。
IRBuilder的作用其实比较纯粹,它的存在就是为了让我们方便快捷地创建IR指令(Instruction)。
注意,IRBuilder并不是全能的,它并没有把所有与创建IR有关的API都集成进来。如果要使用其它未集成的API,可以在创建好指令之后,直接对指令进行操作,比如直接调用LoadInst::setVolatile()等各种Instruction的成员函数。
所以,就算没有IRBuilder,我们照样能用LLVM提供的“原始的”C++ API来创建出所有的Instruction。使用IRBuilder,只是能让我们用稍微少点的代码且稍微快捷一点地创建出Instruction而已。
由以上可以看出,IRBuilder就是一个工具,它提供了一套统一的API,以便用户用来创建IR指令(Instruction),并插入到代码块中。插入的位置可以是在代码块的结尾处,也可以是在代码块中的其它位置。当每次插入Intruction之后,IRBuilder都会更新并记录下新的插入点,以便下一次能插到正确的位置。
另外,IRBuilder还能用来方便地创建数学运算指令,包括整数和float等数学运算指令。
从前面的例子程序及其描述中,我们已经知道IRBuilder是个模板类(示例):
template <typename FolderTy = ConstantFolder,
typename InserterTy = IRBuilderDefaultInserter>
class IRBuilder : public IRBuilderBase {
...
};
第一个模板参数指定了用于创建常量的工具类(Folder)。如果未指定,则默认创建的是一个“最简折叠常量”(Minimally Folded Constant),可以简单地理解为常量表达式的最终结果。
第二个模板参数指定了用于插入Instruction的工具类(Inserter)。它就像一个钩子(Hook),是用户定制过的用来插入Instruction的Hook。它在每次IRBuilder向代码块中插入新的Instruction的时候调用。如果未指定,则默认使用IRBuilderDefaultInserter,这个Hook指定了“每次插入的位置总是当前最新的插入点”,即没有做位置上的偏移或跳跃。IRBuilderDefaultInserter的源代码如下(示例):
class IRBuilderDefaultInserter {
public:
virtual ~IRBuilderDefaultInserter();
virtual void InsertHelper(Instruction *I, const Twine &Name,
BasicBlock *BB,
BasicBlock::iterator InsertPt) const {
if (BB) BB->getInstList().insert(InsertPt, I);
I->setName(Name);
}
};
IRBuilder的工作方式是,首先指定当前代码块,然后逐指令插入(如调用 IRBuilder.CreateAdd 插入一条加法指令)。当完成了一个代码块后,通过IRBuilder.SetInsertPoint() 更改当前代码块。IRBuilder在其内部记录了当前的代码块(BasicBlock),以及当前代码块中的当前指令。每次插入新的指令时,总是在当前指令的后面插入。
如果一条指令会生成一个结果(即一个新的静态单赋值 Static Single Assignment 变量),则意味着IRBuilder.CreateXxxx()就有一个返回值。该返回值的类型为 llvm::Value,可以用于代表这个SSA变量。如以下代码创建了一条加法指令,并把其结果用在return指令中(示例):
Value* arg1 = function->getArg(0);
ConstantInt* three = builder.getInt32(3);
Value* result = builder.CreateMul(arg1, three, "multiplyResult");
builder.CreateRet(result);
(完整源码请参考:https://github.com/wuzhanglin/llvm-IR-examples/blob/main/07.ArithmeticStatement/HelloArithmeticStatement.cpp。)
其生成的IR代码如下(示例):
%multiplyResult = mul i32 %a, 3
ret i32 %multiplyResult
本章,我们简单地分析了一下LLVM API中的IRBuilder的作用和用法。
References