LLVM汇编语言是一种 静态单赋值(SSA) 的中间表示,提供了类型安全检查,低层次的操作符,灵活和清晰表达‘几乎所有’高级语言的能力。同时,它也是LLVM编译算法中各个阶段的一种通用代码表达形式。
根据 其他IR(比如TVM IR 、python ast、gimple ir) 使用 llvm提供的API创建 LLVMir来表征代码
LLVM中的模块(Module),代表了一块代码。它是一个比较完整独立的代码块,是一个最小的编译单元。
需要注意的是,它跟我们平常说的“程序模块”不一样。程序模块是一个更大的集合,包含了很多个编译单元;而LLVM中的模块(Module),我们可以把它初步理解为一个编译单元。
在LLVM IR的基本概念(构件)中,模块是一个组合,它包含了其它更小的基本构件。比如,它可以包含的基本构件有全局变量globalval、函数Function、数据结构Struct等。
// HelloModule.cpp
#include "llvm/IR/Module.h"
#include "llvm/IR/LLVMContext.h"
using namespace llvm;
int main(int argc, char* argv[])
{
LLVMContext context; // 代码上下文管理器
Module* module = new Module("HelloModule", context);
module->print(outs(), nullptr);
return 0;
}
模块也好,函数也好,都是LLVM IR代码。LLVM提供了一个叫IRBuilder的工具来帮助我们创建IR代码。
当然,如果要创建一个函数,我们还需要用到其它工具,比如llvm::Function、llvm::FunctionType等。
我们知道,函数也是有类型的,而llvm::FunctionType就是用来创建(FunctionType::get)函数的类型的(返回值类型,输入参数类型)。
创建函数之前,需要先创建( Type::getVoidTy Type::getxxx)类型(void、int、float、void*、int*、float*…),然后才能用llvm::Function::Create创建函数。
IR创建完成后可以使用 llvm::verifyFunction 来检验一个LLVM模块是否是“良好结构的”。
// HelloFunction.cpp
#include "llvm/IR/Function.h" // Function
#include "llvm/IR/IRBuilder.h" // IRBuilder 代码构建器
#include "llvm/IR/LLVMContext.h"// LLVMContext 代码上下文 (记录一些全局信息, 各种作用域scope等)
#include "llvm/IR/Module.h" // Module 模块
#include "llvm/IR/Verifier.h" // Verifier IR验证
using namespace llvm;
int main(int argc, char* argv[])
{
LLVMContext context; // 上下文 管理器
IRBuilder<> builder(context);// IR构建器
// Create a module
Module* module = new Module("HelloModule", context); // source_filename = "HelloModule" 模块id ModuleID
// Add a function
Type* voidType = Type::getVoidTy(context); // 创建 void 类型 类型变量
FunctionType* functionType = FunctionType::get(voidType, false); // 创建函数类型 (void)()
// 函数类型 函数作用范围(链接类型) 函数名 所属代码模块
Function* function = Function::Create(functionType, GlobalValue::ExternalLinkage, "HelloFunction", module);
// Print the IR
verifyFunction(*function); // verifyFunction 验证函数IR正确性
// llvm::verifyFunction 作用是检查 创建的函数是否正确,确保它是符合编程语言规范的,否则会报错
module->print(outs(), nullptr);
return 0;
}
// 全局标识符(例如函数、全局变量)会以``‘@’`` 开始,而本地标识符(例如寄存器名、类型名)会以 ‘%’ 开始。
/*
; ModuleID = 'HelloModule'
source_filename = "HelloModule"
declare void @HelloFunction() // @ 全局标识符 HelloFunction
*/
//LLVM中的保留字和其他语言中保留字非常相似。
//有对不同的操作码的关键字 ( ‘add’, ‘mul’, ‘bitcast’, ‘ret’ 等 )、基本类型名 ( ‘void’, ‘i32’ 等)、还有其他。
//这些保留字不会和变量名冲突,由于他们没有一个是以特殊字符开头的( '%' 或 '@' )。
//%result = mul i32 %X, 8 // 局部变量 mul 8 得到结果变量 result
空类型 void
整数类型 Integer Type iN
i1 a single-bit integer.
i32 a 32-bit integer.
i1942652 a really big integer of over 1 million bits.
half 16-bit floating point value
float 32-bit floating point value
double 64-bit floating point value
fp128 128-bit floating point value (112-bit mantissa)
[4 x i32]* A pointer to array of four i32 values.
i32 (i32*) * A pointer to a function that takes an i32*, returning an i32.
i32 addrspace(5)* A pointer to an i32 value that resides in address space #5.
<4 x i32> Vector of 4 32-bit integer values.
<8 x float> Vector of 8 32-bit floating-point values.
<2 x i64> Vector of 2 64-bit integer values.
<4 x i64*> Vector of 4 pointers to 64-bit integer values.
[40 x i32] Array of 40 32-bit integer values.
[41 x i32] Array of 41 32-bit integer values.
[4 x i8] Array of 4 8-bit integer values.
[3 x [4 x i32]] 3x4 array of 32-bit integer values.
[12 x [10 x float]] 12x10 array of single precision floating point values.
[2 x [3 x [4 x i16]]] 2x3x4 array of 16-bit integer values.
i32 (i32) 函数 输入一个 i32类型参数 返回 i32类型 int(int)
float (i16, i32 ) * 函数指针类型 指向一个函数 输入 i16类型和 指向i32类型指针类型参数 返回 float 类型
i32 (i8, …) 一个变参数 vararg 类型函数function 输入至少一个 i8类型指针 (char in C), 返回一个 integer. 可以为 printf 函数类型
{i32, i32} (i32) 函数 输入一个 i32类型参数, 返回一个结构体structure 类型 包含两个 i32类型的结构体
{ } ; 一般结构体类型 struct type
<{ }> ; 打包结构体类型 packed struct type
{ i32, i32, i32 } 包含3个 i32类型值 的结构体类型
{ float, i32 (i32) * } 包含两个值的结构体类型,一个是 float 类型,第二个是指针类型 指向一个函数 输入一个 i32, 返回一个 i32.
<{ i8, i32 }> 打包结构体类型 大小为 5 bytes .
布尔常量 Boolean constants false / true
整数常量 Integer constants 1/2/4/10 builder.getInt32
浮点数常量 Floating point constants 1.25/1.23421e+2
空指针常量 Null pointer constants null
结构体常量 Structure constants {i32 4, float 17.0}
数组常量 Array constants [ i32 42, i32 11, i32 74 ] c"Hello World\0A\00"
向量常量 Vector constants < i32 42, i32 11, i32 74, i32 100 >
Type *voidType = Type::getVoidTy(module->getContext()); // void 类型
Type *int32Type = Type::getInt32Ty(module->getContext()); // int 类型
Type *int8PtrType = Type::getInt8PtrTy(module->getContext()); // int8* 类型 char* 类型
Type *int8PtrPtrType = int8PtrType->getPointerTo(); // int8** ;类型 char**
Constant *int32Zero = ConstantInt::get(int32Type, 0, true); // 常量 0
ConstantInt* value66 = builder.getInt32(66); // 获取常量 66
ConstantInt* zero = builder.getInt32(0); // 获取一个整数常量 0
函数(Function)是由基本的逻辑代码块(Basic Block)组成的。
为了简单起见,我们就把“逻辑代码块”简称为“代码块”吧。
一个代码块只有一个起点(entry)和终点。代码块的主要部分当然是代码,即一系列的IR指令(代码)。
最后一条IR指令就是结束指令。代码块的起点可以是一个标签,它指示了下一行代码的位置。
LLVM提供了一个名为BasicBlock的工具来创建代码块 BasicBlock::Create 。当然,在构建代码块的过程中,我们还需要用到通用的构建工具IRBuilder。
// HelloBlock.cpp
#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"
#include "llvm/IR/Verifier.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* voidType = Type::getVoidTy(context);// 获取 void类型
FunctionType* functionType = FunctionType::get(voidType, false); // 获取 void() 函数类型
Function* function = Function::Create(functionType, GlobalValue::ExternalLinkage, "HelloFunction", module);// 创建函数
// Create a block
BasicBlock* block = BasicBlock::Create(context, "entry", function); // 创建 函数入口代码块
builder.SetInsertPoint(block); // 设置当前 处理的代码块 (之后的 IR构建在这个block中)切换到其他blcok 需要使用这个 SetInsertPoint 设置 新的 block
// Print the IR
verifyFunction(*function);
module->print(outs(), nullptr);
return 0;
}
/*
; ModuleID = 'HelloModule'
source_filename = "HelloModule"
define void @HelloFunction() {
entry: ; 函数入口代码块
}
*/
全局变量(Global Variable)是在一个模块(Module)之内全局可见的变量,也就是说模块内所有的函数都能用它。
LLVM提供了 Module::getOrInsertGlobal() 函数来创建全局变量,以及 Module::getNamedGlobal() 来找到一个全局变量。
创建全局变量之后,我们可以配置它的属性,如链接类型、内存对齐模型等。
全局变量在链接的时候,到底是指向同一个全局变量,还是多个不同的全局变量,是由链接类型决定的。
这里说的“多个不同的全局变量”,意思是其名称相同,但是有“多个分身”,“分身”之间互不影响。
链接类型 | 用途 |
---|---|
ExternalLinkage | 模块外部可见的函数 |
AvailableExternallyLinkage | 只在查询而非代码生成的时候外部可见 |
LinkOnceAnyLinkage | 函数在链接(或内连inline)的时候仅保存一份实现 |
LinkOnceODRLinkage | 同上,但有些链接属性可以用类似的属性替换 |
WeakAnyLinkage | 函数在链接的时候仅保存一份实现(弱链接) |
WeakODRLinkage | 同上,但有些链接属性可以用类似的属性替换 |
AppendingLinkage | 这是特殊的链接类型,只适用于全局数组 |
InternalLinkage | 链接时若发现有重命的静态函数,则进行重命名 |
PrivateLinkage | 只做内部链接,但不会添加到符号表中 |
ExternalWeakLinkage | 外部弱链接 |
CommonLinkage | 由编译器(链接器)自动选择链接类型 |
// HelloGlobalVariable.cpp
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.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 global variable
module->getOrInsertGlobal("helloGlobalVariable", Type::getInt32Ty(context)); // 模块中创建一个名为helloGlobalVariable int32类型的全局变量
GlobalVariable* globalVariable = module->getNamedGlobal("helloGlobalVariable"); // 获取该全局变量
globalVariable->setLinkage(GlobalValue::CommonLinkage); // 设置全局变量的 链接类型
globalVariable->setAlignment(MaybeAlign(4));// 设置 全局变量的 内存对齐属性
// Add a function
Type* voidType = Type::getVoidTy(context);
FunctionType* functionType = FunctionType::get(voidType, false);
Function* function = Function::Create(functionType, GlobalValue::ExternalLinkage, "HelloFunction", module);
// Create a block
BasicBlock* block = BasicBlock::Create(context, "entry", function);
builder.SetInsertPoint(block); // 设置当前 处理的代码块
// Print the IR
verifyFunction(*function);
module->print(outs(), nullptr);
return 0;
}
/*
; ModuleID = 'HelloModule'
source_filename = "HelloModule"
@helloGlobalVariable = common global i32, align 4 ; 全局变量
define void @HelloFunction() {
entry:
}
*/
// HelloReturn.cpp
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.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 global variable
module->getOrInsertGlobal("helloGlobalVariable", builder.getInt32Ty());
GlobalVariable* globalVariable = module->getNamedGlobal("helloGlobalVariable");
globalVariable->setLinkage(GlobalValue::CommonLinkage);
globalVariable->setAlignment(MaybeAlign(4));
// Add a function
FunctionType* functionType = FunctionType::get(builder.getInt32Ty(), 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); // 获取一个整数常量 0
builder.CreateRet(zero); //创建返回语句 return 0;
// Print the IR
verifyFunction(*function);
module->print(outs(), nullptr);
return 0;
}
/*
; ModuleID = 'HelloModule'
source_filename = "HelloModule"
@helloGlobalVariable = common global i32, align 4
define i32 @HelloFunction() {
entry:
ret i32 0 ; 返回语句 return 0
}
*/
// HelloFunctionArguments.cpp
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include
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 global variable
module->getOrInsertGlobal("helloGlobalVariable", builder.getInt32Ty());
GlobalVariable* globalVariable = module->getNamedGlobal("helloGlobalVariable");
globalVariable->setLinkage(GlobalValue::CommonLinkage);
globalVariable->setAlignment(MaybeAlign(4));
// Add a function
std::vector<Type*> parameters(2, builder.getInt32Ty()); // 类型向量
FunctionType* functionType = FunctionType::get(builder.getInt32Ty(), parameters, false);// 创建函数类型 i32(i32,i32)
Function* function = Function::Create(functionType, GlobalValue::ExternalLinkage, "HelloFunction", module);
// Set arguments for the function
function->getArg(0)->setName("a"); // 为函数 创建参数 名为 a
function->getArg(1)->setName("b"); // 为函数 创建参数 名为 b
// 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
verifyFunction(*function);
module->print(outs(), nullptr);
return 0;
}
/*
; ModuleID = 'HelloModule'
source_filename = "HelloModule"
@helloGlobalVariable = common global i32, align 4
define i32 @HelloFunction(i32 %a, i32 %b) { ; 输入两个i32类型参数(名为 a,b)的 函数
entry:
ret i32 0
}
*/
一个基本的逻辑代码块(Basic Block)是由一系列的指令(Instruction)组成的。
一条指令主要是为了完成某个任务(操作),比如一个算术运算。
一条指令可以是一个简单的语句,比如一个算术运算语句。
在LLVM中,一个简单的算术运算需要用到操作符和操作数。例如,一个乘法运算用到了一个乘法操作符和两个乘数。
所以,要创建一个乘法运算语句,我们首先要得到两个操作数,比如它可以来自于函数的参数、数字常量等等。
我们知道,乘法操作是一个二元操作,IRBuilder里面提供了很多API,可以用来方便地创建二元操作语句。
// HelloArithmeticStatement.cpp
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include
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 global variable
module->getOrInsertGlobal("helloGlobalVariable", builder.getInt32Ty());
GlobalVariable* globalVariable = module->getNamedGlobal("helloGlobalVariable");
globalVariable->setLinkage(GlobalValue::CommonLinkage);
globalVariable->setAlignment(MaybeAlign(4));
// Add a function
std::vector<Type*> parameters(2, builder.getInt32Ty());
FunctionType* functionType = FunctionType::get(builder.getInt32Ty(), parameters, false); // int HelloFunction(int a, int b);
Function* function = Function::Create(functionType, GlobalValue::ExternalLinkage, "HelloFunction", module);
// Set arguments for the function
function->getArg(0)->setName("a");
function->getArg(1)->setName("b");
// Create a block
BasicBlock* block = BasicBlock::Create(context, "entry", function);
builder.SetInsertPoint(block); // 设置当前 处理的代码块
// Create an arithmetic statement
Value* arg1 = function->getArg(0); // 获取 函数第一个参数 作为一个操作数
ConstantInt* three = builder.getInt32(3); // 创建一个常量 3
// 都是 三段码的形式 没有嵌套的表达式 一个运算后就会产生一个目标变量赋值语句
Value* result = builder.CreateMul(arg1, three, "multiplyResult"); // 构建语句 multiplyResult = a* 3
// Add a return
builder.CreateRet(result); // IR构建器插入 返回语句 return multiplyResult;
// Print the IR
verifyFunction(*function);
module->print(outs(), nullptr);
return 0;
}
/*
; ModuleID = 'HelloModule'
source_filename = "HelloModule"
@helloGlobalVariable = common global i32, align 4
define i32 @HelloFunction(i32 %a, i32 %b) {
entry:
%multiplyResult = mul i32 %a, 3 ; multiplyResult = a* 3;
ret i32 %multiplyResult ; return multiplyResult;
}
*/
CreateICmpSGT 整数 比较语句 有符号 singed great then > compare.result = arg > value33
Value* condition = builder.CreateICmpSGT(arg, value33, “compare.result”)
builder.CreateCall(函数类型 FunctionType, 函数 Function, 函数实参)
builder.CreateCall(calculatorWriteFunctionType, calculatorWriteFunction, {value}) // call void @calc_write(i32 %7)
CreateAlloca 指令在栈上申明一个局部变量。
注意用alloca 指令申明的变量,其实得到是变量的地址。
如果要访问它,我们需要用 CreateStore 和 CreateLoad 指令。
分配变量 Value* bPtr = builder.CreateAlloca(builder.getInt32Ty() 类型, nullptr 初始值 , “b.address” 指针变量名)
%b.address = alloca i32, align 4
存储值 变量更新 builder.CreateStore(value66, bPtr); // bPtr = value66;
store i32 %i.incremented, i32 %i.address, align 4
变量载入读取 Value* returnValue = builder.CreateLoad(bPtr, “return.value”); // return.value = bPtr;
%3 = load i32, i32 %i.address, align 4
无条件跳转语句 builder.CreateBr(returnBlock 目标代码块);
br label %for.condition
条件跳转语句 builder.CreateCondBr(condition 条件变量, thenBlock 真 执行分支代码块, elseBlock 假执行分支代码块);
br i1 %for.condition.compare.result, label %for.body, label %for.end
PHINode* phi = builder.CreatePHI(builder.getInt32Ty(), 2); // 创建 phi 变量 类型 int32 两个候选 分支变量
phi->addIncoming(value66, thenBlock); // 为phi 变量添加候选 分支变量组合 如果执行了 thenBlock 那么 phi = value66;
phi->addIncoming(value77, elseBlock); // 为phi 变量添加候选 分支变量组合 如果执行了 elseBlock 那么 phi = value77;
GlobalVariable* str = new GlobalVariable(…);
GlobalVariable* str = new GlobalVariable(*module,
strText->getType(),
/isConstant=/true,
GlobalValue::PrivateLinkage,
strText,
Twine(variable).concat(“.str”));
// 创建全局字符串变量
@a.str = private constant [2 x i8] c"a\00"
Value* ptr = builder.CreateInBoundsGEP(str, {int32Zero, int32Zero}, “ptr”);
i8* getelementptr inbounds ([4 x i8], [4 x i8]* @abc.str, i32 0, i32 0)
一个if-else语句包含了一个条件判断以及两个逻辑分支。
最终会运行哪个分支的代码,取决于条件判断的结果为真还是假。而“条件”则一般是一个比较表达式 Compare。
以下述c代码为例子,创建 对应的LLVM IR
// Test.c
int Test(int a)
{
int b; //可变变量b,当然它也是一个局部变量,我们可以在 函数 栈 上创建一个可变变量。
// builder.CreateAlloca(Type类型, Value值, 名字) 返回的是 改变量的 指针
// 然后使用 load 获取改变量的值 store value, var 改变该变量 存数据 到该变量上
if (a > 33)
{
b = 66;
}
else
{
b = 77;
}
return b;
}
对应 LLVM ir 构建方式
// HelloIfElse.cpp
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include
using namespace llvm;
int main(int argc, char* argv[])
{
LLVMContext context;
IRBuilder<> builder(context);
// Create a module
Module* module = new Module("Test.c", context); // 模块文件名为 Test.c
// Add a function
std::vector<Type*> parameters(1, builder.getInt32Ty());
FunctionType* functionType = FunctionType::get(builder.getInt32Ty(), parameters, false); // 函数类型 包含一个i32输入参数 返回值类型为 i32
Function* function = Function::Create(functionType, GlobalValue::ExternalLinkage, "Test", module); // 创建一个 函数 int test(int a)
// Add an argument to the function
Value* arg = function->getArg(0);
arg->setName("a"); // 设置函数参数名
// Add some basic blocks to the function 创建函数内用到的代码块 scope代码块
BasicBlock* entryBlock = BasicBlock::Create(context, "entry", function); // 函数入口代码块 entry 也是主函数 score 代码块
BasicBlock* thenBlock = BasicBlock::Create(context, "if.then", function); // if then 分支代码块
BasicBlock* elseBlock = BasicBlock::Create(context, "if.else", function); // if else 分支代码块
BasicBlock* returnBlock = BasicBlock::Create(context, "if.end", function); // if-else 后的代码块
// Fill the "entry" block (1):
// builder.SetInsertPoint 插入 入口代码块 主函数代码块
// int b;
builder.SetInsertPoint(entryBlock); // 当前处理的的代码块
// 函数栈 上分配 一个变量 返回变量指针 i32 类型变量 值为 nullptr 名为 b.address
Value* bPtr = builder.CreateAlloca(builder.getInt32Ty(), nullptr, "b.address");
// Fill the "entry" block (2):
// if (a > 33)
ConstantInt* value33 = builder.getInt32(33); // 创建常量 33
Value* condition = builder.CreateICmpSGT(arg, value33, "compare.result"); //创建 比较语句 compare.result = a > 33
builder.CreateCondBr(condition, thenBlock, elseBlock); // 创建 控制流 条件跳转 if compare.result thenBlock; else elseBlock;
// Fill the "if.then" block:
// b = 66;
builder.SetInsertPoint(thenBlock); // 设置当前 处理的代码块 thenBlock 条件正确会执行的 代码块
ConstantInt* value66 = builder.getInt32(66); // 获取常量 66
builder.CreateStore(value66, bPtr); // 栈变量 存储 b.address = 66;
builder.CreateBr(returnBlock); // 创建 直接跳转 无条件跳转语句 跳转到 if-else 后面的 returnBlock 代码块
// Fill the "if.else" block:
// b = 77;
builder.SetInsertPoint(elseBlock); // 设置当前 处理的代码块 elseBlock 条件错误会执行的 代码块
ConstantInt* value77 = builder.getInt32(77); // 获取常量 77
builder.CreateStore(value77, bPtr); // 栈变量 存储 b.address = 77;
builder.CreateBr(returnBlock);
// Fill the "if.end" block:
// return b;
builder.SetInsertPoint(returnBlock); // 设置当前 处理的代码块 returnBlock if-else 后的代码块
Value* returnValue = builder.CreateLoad(bPtr, "return.value"); // 创建栈变量 载入 语句 return.value = b.address;
builder.CreateRet(returnValue); // 当前 代码块 插入 返回语句 return return.value;
// Print the IR
verifyFunction(*function);
module->print(outs(), nullptr);
return 0;
}
; ModuleID = 'Test.c'
source_filename = "Test.c"
define i32 @Test(i32 %a) {
entry:
%b.address = alloca i32, align 4 ; 分配栈变量 b.address = alloca
%compare.result = icmp sgt i32 %a, 33 ; 比较语句 compare.result = a > 33
br i1 %compare.result, label %if.then, label %if.else ; 条件跳转语句 if compare.result if.then; else if.else;
if.then: ; preds = %entry 父 代码块为 entry
store i32 66, i32* %b.address, align 4 ; 栈变量存储语句 *b.address = 66
br label %if.end ; 无条件跳转 到 if-else 之后的代码块 if.end
if.else: ; preds = %entry
store i32 77, i32* %b.address, align 4 ; 栈变量存储语句 *b.address = 77
br label %if.end
if.end: ; preds = %if.else, %if.then 父 代码块为 if.else if.then 入边所在的代码块
%return.value = load i32, i32* %b.address, align 4 ; 栈变量读取语句 return.value = *b.address;
ret i32 %return.value ; 返回语句 return return.value
}
一个if-else语句包含了一个条件判断以及两个逻辑分支。最终会运行哪个分支的代码,取决于条件判断的结果为真还是假。而“条件”则一般是一个比较表达式。
在很多情况下,控制流只是为了给某一个变量赋值,而phi 指令,则可以根据控制流来选择合适的值。它的用法如下(示例):
%value = phi i32 [66, %branch1], [77, %branch2], [88, %branch3] ; 返回类型 i32 , ... 控制流变量
可以看到phi指令可以接收多个输入参数,参数的个数也不是固定的。
第一个参数表示的是phi指令的返回值类型,如在以上示例中为i32。
接下来的每一个参数都是一个数组,代表了每一个分支及其对应的返回值。
例如,如果前一步执行的是branch1分支,则返回值为66;当执行的是branch2,则返回值为77;以此类推…
// 创建PHI
PHINode * llvm::IRBuilderBase::CreatePHI(Type * Ty, unsigned NumReservedValues, const Twine & Name = ""); // 返回类型, 候选值数量, phi变量名
// 以及向phi指令中添加条件返回值:
void llvm::PHINode::addIncoming(Value * V, BasicBlock * BB); // 值, 条件分子代码块
C函数跟上面 用到的是一样的,只不过 这次在IR代码中用到了phi指令而已。
// HelloIfElsePhi.cpp
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include
using namespace llvm;
int main(int argc, char* argv[])
{
LLVMContext context;
IRBuilder<> builder(context);
// Create a module
Module* module = new Module("Test.c", context);
// Add a function
std::vector<Type*> parameters(1, builder.getInt32Ty());
FunctionType* functionType = FunctionType::get(builder.getInt32Ty(), parameters, false); // 函数类型 int Test (int a)
Function* function = Function::Create(functionType, GlobalValue::ExternalLinkage, "Test", module);
// Add an argument to the function
Value* arg = function->getArg(0);
arg->setName("a"); // 函数参数 a
// Add some basic blocks to the function
BasicBlock* entryBlock = BasicBlock::Create(context, "entry", function); // 入口代码块
BasicBlock* thenBlock = BasicBlock::Create(context, "if.then", function);// 条件成立代码块
BasicBlock* elseBlock = BasicBlock::Create(context, "if.else", function);// 条件不成立代码块
BasicBlock* returnBlock = BasicBlock::Create(context, "if.end", function);// 条件语句后的代码块
// Fill the "entry" block (1):
// int b;
builder.SetInsertPoint(entryBlock); // 设置当前的代码快为 入口代码块
Value* b = builder.CreateAlloca(builder.getInt32Ty(), nullptr, "b.address"); // 分配栈变量 b.address int* = malloc()
// Fill the "entry" block (2):
// if (a > 33)
ConstantInt* value33 = builder.getInt32(33);
Value* condition = builder.CreateICmpSGT(arg, value33, "compare.result"); // 条件语句 CreateICmpSGT compare.result = a > 33
builder.CreateCondBr(condition, thenBlock, elseBlock); // 创建 条件跳转语句 CreateCondBr
// Fill the "if.then" block:
// b = 66;
builder.SetInsertPoint(thenBlock); // 设置当前的代码快为 条件true分支代码块
ConstantInt* value66 = builder.getInt32(66); // 创建 value66 = 66;
builder.CreateBr(returnBlock); // 跳转到后面的代码块
// Fill the "if.else" block:
// b = 77;
builder.SetInsertPoint(elseBlock); // 设置当前的代码快为 条件false分支代码块
ConstantInt* value77 = builder.getInt32(77); // 创建 value77 = 77;
builder.CreateBr(returnBlock);
// Fill the "if.end" block with phi instruction:
// return b;
builder.SetInsertPoint(returnBlock);// 设置当前的代码快为 最后的代码快
PHINode* phi = builder.CreatePHI(builder.getInt32Ty(), 2); // 创建 phi 变量 类型 int32 两个候选 分支变量
phi->addIncoming(value66, thenBlock); // 为phi 变量添加候选 分支变量组合 如果执行了 thenBlock 那么 phi = value66;
phi->addIncoming(value77, elseBlock); // 为phi 变量添加候选 分支变量组合 如果执行了 elseBlock 那么 phi = value77;
builder.CreateStore(phi, b); // 创建 栈变量 存储语句 *b.address = phi;
Value* returnValue = builder.CreateLoad(b, "return.value"); // 创建 栈变量 读取语句 return.value = *b.address
builder.CreateRet(returnValue); // 创建 返回值 语句 return return.value;
// Print the IR
verifyFunction(*function);
module->print(outs(), nullptr);
return 0;
}
; ModuleID = 'Test.c'
source_filename = "Test.c"
define i32 @Test(i32 %a) {
entry:
%b.address = alloca i32, align 4
%compare.result = icmp sgt i32 %a, 33
br i1 %compare.result, label %if.then, label %if.else
if.then: ; preds = %entry
br label %if.end
if.else: ; preds = %entry
br label %if.end
if.end: ; preds = %if.else, %if.then
%0 = phi i32 [ 66, %if.then ], [ 77, %if.else ] ; 分支变量汇合节点 %0 = 66 if if.then; else 77 if.else
store i32 %0, i32* %b.address, align 4 ; 栈变量保存结果 *b.address = %0
%return.value = load i32, i32* %b.address, align 4 ; 创建返回值变量 return.value = *b.address
ret i32 %return.value
}
一个for语句包含了条件判断以及循环执行的任务。具体循环多少次,取决于条件判断的结果。而“条件”则包含了循环的起点、终止条件、步进长度。
这里的for循环语句IR,其实跟前面章节的if-else语句有些相似的地方。它们都需要用到 比较指令、跳转指令、栈上的变量等。
// Test.c
int Test(int a)
{
int b = 0; // 栈上的变量
for (int i = 0; i < a; i++) // 比较指令、跳转指令
{
b = b + i; ; for body
}
return b;
}
// HelloLoop.cpp
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include
using namespace llvm;
int main(int argc, char* argv[])
{
LLVMContext context;
IRBuilder<> builder(context);
// Create a module
Module* module = new Module("Test.c", context);
// Add a function with argument
// int Test(int a)
std::vector<Type*> parameters(1, builder.getInt32Ty()); // 参数类型数组
FunctionType* functionType = FunctionType::get(builder.getInt32Ty(), parameters, false); // int(int)
Function* function = Function::Create(functionType, GlobalValue::ExternalLinkage, "Test", module);
Value* a = function->getArg(0);
a->setName("a");
// Add some basic blocks to the function
BasicBlock* entryBlock = BasicBlock::Create(context, "entry", function); // 函数 入口代码块
BasicBlock* forConditionBlock = BasicBlock::Create(context, "for.condition", function); // for 条件代码块
BasicBlock* forBodyBlock = BasicBlock::Create(context, "for.body", function); // for 循环体代码块
BasicBlock* forIncrementBlock = BasicBlock::Create(context, "for.increment", function); // for 计数代码块
BasicBlock* forEndBlock = BasicBlock::Create(context, "for.end", function); // 结束 代码块
// entry -> for.condition
// for.condition -> for.body -> for.increment -> for.condition
// -> for.end
// Fill the "entry" block (1):
// int b = 0;
builder.SetInsertPoint(entryBlock); // 处理的当前 代码块 entry
Value* bPtr = builder.CreateAlloca(builder.getInt32Ty(), nullptr, "b.address"); // 创建栈变量
builder.CreateStore(builder.getInt32(0), bPtr); // 初始化为1 *b.address=0;
// Fill the "entry" block (2):
// for (int i = 0; ...)
Value* iPtr = builder.CreateAlloca(builder.getInt32Ty(), nullptr, "i.address"); // 创建 for 迭代 变量 也是临时变量 栈变量
builder.CreateStore(builder.getInt32(0), iPtr); // i=0;
builder.CreateBr(forConditionBlock);// 无条件跳转到 条件判断 代码块
// 控制流 1
// entry -> for.condition
// Fill the "for.condition" block:
// for (... i < a; ...)
builder.SetInsertPoint(forConditionBlock);// 处理的当前 代码块 for.condition
Value* i0 = builder.CreateLoad(iPtr); // 载入栈变量 循环迭代变量
Value* forConditionCompare = builder.CreateICmpSLT(i0, a, "for.condition.compare.result"); // 创建比较语句 i
builder.CreateCondBr(forConditionCompare, forBodyBlock, forEndBlock); // 创建条件跳转语句
// 控制流 2
// for.condition ->cond==true for.body
// ->cond==false for.end
// Fill the "for.body" block:
// b = b + i;
builder.SetInsertPoint(forBodyBlock);// 处理的当前 代码块 for.body
Value* b0 = builder.CreateLoad(bPtr);// 读取栈变量 b =*bptr
Value* i1 = builder.CreateLoad(iPtr);// 读取栈变量 i =*iptr
Value* addResult = builder.CreateAdd(b0, i1, "add.result"); // 创建add运算并赋值 add.result = b +i
builder.CreateStore(addResult, bPtr);// 存储栈变量 *bptr = add.result
builder.CreateBr(forIncrementBlock); // 创建无条件跳转
// 控制流 3
// for.body -> for.increment
// Fill the "for.increment" block:
// for (... i++)
builder.SetInsertPoint(forIncrementBlock);// 处理的当前 代码块 for.increment
Value* i2 = builder.CreateLoad(iPtr); // 读取栈变量 i =*iptr
Value* incrementedI = builder.CreateAdd(i2, builder.getInt32(1), "i.incremented"); // 创建迭代变量自增语句 i.incremented = i++
builder.CreateStore(incrementedI, iPtr); // 更新 迭代变量 *iPtr = i.incremented
builder.CreateBr(forConditionBlock); // 创建无条件跳转
// 控制流 4
// for.increment -> for.condition
// Fill the "for.end" block:
// return b;
builder.SetInsertPoint(forEndBlock); // 处理的当前 代码块 for.end
Value* returnValue = builder.CreateLoad(bPtr, "return.value"); // 读取栈变量 return.value =*bptr
builder.CreateRet(returnValue); // 创建返回语句 return return.value;
// Print the IR
verifyFunction(*function);
module->print(outs(), nullptr);
return 0;
}
Pass是LLVM中很重要的部分。Pass大体上可以理解为一个“处理”,它处理的对象是IR代码。
LLVM对代码的分析、转换和优化等处理工作都是由Pass来做的。
LLVM以流水线的方式把各个Pass组合起来,让它们成为一个有序的流程。
LLVM Pass可以处理的对象有模块(Module)、函数(Function)、循环(Loop),甚至函数调用栈(Function Call Graph)等等。
// MyPass.cpp
#include "llvm/IR/PassManager.h" // 遍管理器
#include "llvm/Passes/PassBuilder.h" // 遍构建器
#include "llvm/Passes/PassPlugin.h" // 遍插件
#include "llvm/Support/raw_ostream.h"
// Only needed for printing
#include
using namespace llvm;
namespace
{
class MyPass : public PassInfoMixin<MyPass>
{
public:
// The first argument of the run() function defines on what level
// of granularity your pass will run (e.g. Module, Function).
// The second argument is the corresponding AnalysisManager
// (e.g ModuleAnalysisManager, FunctionAnalysisManager)
PreservedAnalyses run(Function& function, FunctionAnalysisManager& analysisManager)
{
std::cout << "MyPass in function: " << function.getName().str() << std::endl; // 打印当前遍历到函数名
// Here goes what you want to do with a pass
// Assuming we did not change anything of the IR code
return PreservedAnalyses::all();
}
};
}
// This part is the new way of registering your pass
extern "C" PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK llvmGetPassPluginInfo()
{
return
{
LLVM_PLUGIN_API_VERSION,
"MyPass",
"v0.1",
[](PassBuilder &passBuilder) {
passBuilder.registerPipelineParsingCallback(
[](StringRef name, FunctionPassManager &passManager, ArrayRef<PassBuilder::PipelineElement>) {
if(name == "my-pass")
{
passManager.addPass(MyPass());
return true;
}
return false;
}
);
}
};
}