LLVM IR相关的类及一些用法
模块(llvm::Module)
llvm::Module
表示一个完整的 LLVM IR 单位(对应一个 .ll
或 .bc
文件),包含函数、全局变量、别名、符号表等信息。
创建与加载
#include "llvm/IR/Module.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/IRReader/IRReader.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/MemoryBuffer.h"
// 从内存缓冲区或文件读取
llvm::LLVMContext Context;
llvm::SMDiagnostic Err;
std::unique_ptr<llvm::Module> M = llvm::parseIRFile("input.ll", Err, Context);
if (!M) {
Err.print("MyTool", llvm::errs());
return nullptr;
}
// 或者手动创建空模块
auto ModulePtr = std::make_unique<llvm::Module>("MyModule", Context);
主要方法
- 获取模块名称
StringRef moduleName = M->getName(); // 返回模块名称
插入/删除全局变量、函数
// 添加全局变量
llvm::Type *Int32Ty = llvm::Type::getInt32Ty(Context);
auto *GV = new llvm::GlobalVariable(
*M, // 所属模块
Int32Ty, // 元素类型
false, // 是否常量
llvm::GlobalValue::ExternalLinkage, // 链接类型
llvm::ConstantInt::get(Int32Ty, 0), // 初始值
"myGlobalVar" // 名称
);
// 删除某个全局
GV->eraseFromParent();
遍历函数与全局
for (auto &Func : *M) {
llvm::Function &F = Func;
// 处理函数 F
}
for (auto &G : M->globals()) {
llvm::GlobalVariable &Global = G;
// 处理全局变量 Global
}
验证模块
#include "llvm/IR/Verifier.h"
if (llvm::verifyModule(*M, &llvm::errs())) {
llvm::errs() << "模块验证失败!\n";
}
打印 IR
M->print(llvm::outs(), nullptr);
上下文(llvm::LLVMContext)
llvm::LLVMContext 代表一个 LLVM “上下文”,用于存储全局的单例数据,如常量唯一化表、类型唯一化表等。通常一个应用只使用一个 LLVMContext,但多线程或隔离场景下可创建多个上下文。
常用场景
- 创建类型
llvm::IntegerType *Int32Ty = llvm::Type::getInt32Ty(Context); llvm::PointerType *PtrToInt32 = llvm::Type::getInt32PtrTy(Context, 0);
- 获取函数的上下文
llvm::Function *F = /* 某个函数 */; llvm::LLVMContext &Ctx = F->getContext();
- 创建 IRBuilder
llvm::IRBuilder<> Builder(Context);
类型(llvm::Type 及其子类)
LLVM 中所有的类型都派生自 llvm::Type,常见子类包括 IntegerType、PointerType、ArrayType、VectorType、FunctionType、StructType 等。
基础类型
- Void 类型
llvm::Type *VoidTy = llvm::Type::getVoidTy(Context);
- 标签类型(Label,用于基本块标签)
llvm::Type *LabelTy = llvm::Type::getLabelTy(Context);
- 整数类型
llvm::IntegerType *Int1Ty = llvm::Type::getInt1Ty(Context); llvm::IntegerType *Int8Ty = llvm::Type::getInt8Ty(Context); llvm::IntegerType *Int16Ty = llvm::Type::getInt16Ty(Context); llvm::IntegerType *Int32Ty = llvm::Type::getInt32Ty(Context); llvm::IntegerType *Int64Ty = llvm::Type::getInt64Ty(Context); llvm::IntegerType *Int128Ty = llvm::Type::getInt128Ty(Context);
- 浮点数类型
llvm::Type *HalfTy = llvm::Type::getHalfTy(Context); // 16-bit IEEE 半精度 llvm::Type *BFloatTy = llvm::Type::getBFloatTy(Context); // BFloat16 llvm::Type *FloatTy = llvm::Type::getFloatTy(Context); // 32-bit llvm::Type *DoubleTy = llvm::Type::getDoubleTy(Context); // 64-bit llvm::Type *FP128Ty = llvm::Type::getFP128Ty(Context); // 128-bit
指针类型
用于表示指向某类型的指针,允许指定地址空间(Address Space)。
llvm::PointerType *Int32PtrTy = llvm::Type::getInt32PtrTy(Context, 0); // 默认地址空间0
llvm::PointerType *FloatPtrTy = llvm::Type::getFloatPtrTy(Context, 0);
llvm::PointerType *DoublePtrTy = llvm::Type::getDoublePtrTy(Context, 1); // 地址空间1
也可以通过更通用的接口:
llvm::PointerType *PtrToTy = llvm::PointerType::get(SomeType, AddrSpace);
数组与向量类型
- 数组类型
// 创建一个包含 10 个 i32 元素的数组类型 [10 x i32] llvm::ArrayType *Array10xI32 = llvm::ArrayType::get(Int32Ty, 10);
- 向量类型(SIMD)
// 创建一个包含 4 个 float 的向量类型 <4 x float> llvm::VectorType *Vec4f = llvm::VectorType::get(FloatTy, 4, /*isScalable=*/false);
Scalable 向量可用于 Arm SVE 等架构,若为不可伸缩则设置 isScalable=false。
函数类型
使用 llvm::FunctionType::get 创建函数签名(返回类型 + 参数列表 + 可变参数标志):
llvm::Type *RetTy = Int32Ty;
std::vector<llvm::Type *> Params = { Int32Ty, Int32Ty };
bool IsVarArg = false;
// 创建 i32 foo(i32, i32) 函数类型
llvm::FunctionType *FuncTy = llvm::FunctionType::get(RetTy, Params, IsVarArg);
如果要创建可变参数函数,例如 printf:
llvm::FunctionType *PrintfTy = llvm::FunctionType::get(
Int32Ty, // 返回 int
{ llvm::Type::getInt8PtrTy(Context) }, // 第一个参数:i8*
true // 可变参数
);
结构体类型
- 匿名结构体
// 创建一个匿名结构体 { i32, float, i8* } std::vector<llvm::Type *> Elements = { Int32Ty, FloatTy, llvm::Type::getInt8PtrTy(Context) }; llvm::StructType *AnonStruct = llvm::StructType::get(Context, Elements, /*isPacked=*/false);
- 命名结构体
llvm::StructType *NamedStruct = llvm::StructType::create(Context, "MyStruct"); // 以后再设置元素类型 NamedStruct->setBody(Elements, /*isPacked=*/false);
值(llvm::Value 及子类)
llvm::Value 是 LLVM IR 的根基类,表示 IR 中的“值”(包括常量、参数、指令、函数、基本块等均直接或间接继承自 Value)。
获取类型与名称
- 获取类型
llvm::Value *V = /* 某个 Value */; llvm::Type *Ty = V->getType();
- 获取/设置名称
llvm::StringRef Name = V->getName(); V->setName("new_name");
遍历使用者(Uses)
每个 Value 都维护了一个“使用链表”(Use List),记录哪些 User(例如指令、ConstantExpr 等)在使用该值。可通过迭代器遍历:
for (auto It = V->use_begin(), End = V->use_end(); It != End; ++It) {
llvm::Use &UseRef = *It;
llvm::User *Usr = UseRef.getUser();
// Usr 是某个使用了 V 的 User(通常是 Instruction 或 ConstantExpr)
}
- 获取使用总数
unsigned NumUses = V->getNumUses();
- 替换所有使用
llvm::Value *OldV = /* 旧值 */; llvm::Value *NewV = /* 新值 */; OldV->replaceAllUsesWith(NewV);
全局变量与别名(llvm::GlobalVariable、llvm::GlobalAlias)
llvm::GlobalVariable
表示模块范围的全局变量,有初始值、链接类型、地址空间、对齐等属性。
#include "llvm/IR/GlobalVariable.h"
// 创建一个 i32 全局变量,初始值为 42
llvm::GlobalVariable *GV = new llvm::GlobalVariable(
*ModulePtr, // 所属模块
Int32Ty, // 元素类型
false, // 是否 const
llvm::GlobalValue::ExternalLinkage, // 链接类型
llvm::ConstantInt::get(Int32Ty, 42), // 初始值
"gVar" // 名称
);
// 设置对齐
GV->setAlignment(llvm::MaybeAlign(4));
// 访问初始值
llvm::Constant *Init = GV->getInitializer();
- 常见方法
bool isConstant = GV->isConstant(); GV->setConstant(true); // 将其设为常量 llvm::Type *ElemTy = GV->getValueType(); // 全局变量的元素类型 unsigned AddrSpace = GV->getAddressSpace();
- 删除全局变量
GV->eraseFromParent();
llvm::GlobalAlias
用于创建全局别名(例如将某个全局符号重定向到另一个符号)。常见于编译器实现底层库函数重命名。
#include "llvm/IR/GlobalAlias.h"
// 假设有一个已有的全局函数 F
llvm::Function *F = /* 某个 llvm::Function* */;
// 创建一个别名 aliasName,指向函数 F
llvm::GlobalAlias *GA = llvm::GlobalAlias::create(
F->getFunctionType(), // 别名的函数类型必须与原函数一致
F->getFunctionType()->getPointerTo(F->getAddressSpace()), // 别名的类型
llvm::GlobalValue::ExternalLinkage, // 链接类型
"aliasName", // 别名名称
F, // 被别名的目标
ModulePtr.get() // 所属模块
);
// 删除别名
GA->eraseFromParent();
函数(llvm::Function)
llvm::Function 表示模块中的一个函数,由若干基本块及其指令构成。继承自 GlobalValue 和 Value。
创建函数
#include "llvm/IR/Function.h"
#include "llvm/IR/DerivedTypes.h"
// 准备函数类型
llvm::FunctionType *FuncTy = llvm::FunctionType::get(
/*Result=*/Int32Ty,
/*Params=*/{Int32Ty, Int32Ty},
/*IsVarArg=*/false
);
// 创建函数,放入 Module
llvm::Function *F = llvm::Function::Create(
FuncTy,
llvm::GlobalValue::ExternalLinkage, // 链接类型
"addTwoInts", // 函数名
ModulePtr.get() // 所属模块
);
// 设置函数参数名称
unsigned Idx = 0;
for (auto &Arg : F->args()) {
if (Idx == 0) Arg.setName("a");
else if (Idx == 1) Arg.setName("b");
++Idx;
}
函数属性与调用约定
- 调用约定
F->setCallingConv(llvm::CallingConv::C); // C 调用约定 F->setCallingConv(llvm::CallingConv::Fast); // fastcall
- 添加函数属性
// 在函数返回值上添加属性 F->addFnAttr(llvm::Attribute::NoUnwind); F->addFnAttr(llvm::Attribute::AlwaysInline);
- 查询属性
bool isInline = F->hasFnAttribute(llvm::Attribute::AlwaysInline);
遍历基本块与参数
- 遍历基本块
for (llvm::BasicBlock &BB : *F) { // 处理基本块 BB }
- 或者使用迭代器:
for (auto It = F->begin(), End = F->end(); It != End; ++It) { llvm::BasicBlock &BB = *It; }
- 获取基本块数量
size_t NumBB = F->size();
- 遍历参数
for (llvm::Argument &Arg : F->args()) { // Arg.getName(), Arg.getType() 等 }
- 获取所属模块与上下文
llvm::Module *ParentMod = F->getParent(); llvm::LLVMContext &Ctx = F->getContext();
基本块(llvm::BasicBlock)
llvm::BasicBlock 表示函数体内的一段指令序列,以单一入口且无分支跳入。基本块包含若干指令,最后通常以终止指令(如 ret、br、switch)结尾。
创建与插入
#include "llvm/IR/BasicBlock.h"
// 在函数末尾创建新的基本块
llvm::BasicBlock *BB = llvm::BasicBlock::Create(
Context, // LLVMContext&
"entry", // 基本块名称(可选)
F // 所属函数
);
// 在某个已有基本块之前插入
llvm::BasicBlock *InsertBefore = /* 某个 llvm::BasicBlock* */;
llvm::BasicBlock *BB2 = llvm::BasicBlock::Create(
Context,
"middle",
F,
InsertBefore
);
前驱与后继
要遍历基本块的前驱(pred)和后继(succ),需包含头文件 llvm/IR/CFG.h。
- 遍历前驱
for (auto PI = llvm::pred_begin(BB), PE = llvm::pred_end(BB); PI != PE; ++PI) { llvm::BasicBlock *PredBB = *PI; // 处理前驱 PredBB }
- 遍历后继
for (auto SI = llvm::succ_begin(BB), SE = llvm::succ_end(BB); SI != SE; ++SI) { llvm::BasicBlock *SuccBB = *SI; // 处理后继 SuccBB }
- 获取前驱/后继数量
unsigned NumPred = llvm::pred_size(BB); unsigned NumSucc = llvm::succ_size(BB);
在函数间移动
- 从原函数移除
BB->removeFromParent();
- 插入到新函数
// 假设 NewF 是一个新的 llvm::Function* BB->insertInto(NewF, /*InsertBefore=*/nullptr); // 插入到 NewF 的末尾
- 获取同一函数中前后相邻基本块
llvm::BasicBlock *Prev = BB->getPrevNode(); // 若 BB 是第一个基本块,返回 nullptr llvm::BasicBlock *Next = BB->getNextNode(); // 若 BB 是最后一个基本块,返回 nullptr
常量(llvm::Constant 及子类)
llvm::Constant 是 Value 的子类,表示 LLVM IR 中的常量,如整型、浮点、全局地址、常量表达式等。
整型常量与浮点常量
- 整型常量 ```cpp // 32 位有符号整数常量 123 llvm::ConstantInt CI32 = llvm::ConstantInt::get(Int32Ty, 123, /IsSigned=*/true);
// 布尔常量 llvm::ConstantInt *TrueVal = llvm::ConstantInt::getBool(Context, true);
- 浮点常量
```cpp
llvm::ConstantFP *FPVal = llvm::ConstantFP::get(Context, llvm::APFloat(3.14f));
- 零初始化常量
llvm::Constant *ZeroInt = llvm::ConstantInt::get(Int32Ty, 0); llvm::Constant *NullPtr = llvm::ConstantPointerNull::get(llvm::Type::getInt8PtrTy(Context)); llvm::Constant *ZeroArray = llvm::ConstantAggregateZero::get(Array10xI32);
常量表达式(ConstantExpr)
ConstantExpr 用于在 IR 常量环境下构造各种操作,而无需指令上下文。例如:
#include "llvm/IR/Constants.h"
// 创建一个对指针 varPtr 执行 getelementptr 的常量表达式
llvm::Constant *GEPConst = llvm::ConstantExpr::getGetElementPtr(
/*PointeeType=*/SomeContainerType,
/*Ptr=*/SomeGlobalVar,
/*Indices=*/{ llvm::ConstantInt::get(Int32Ty, 0), llvm::ConstantInt::get(Int32Ty, 2) }
);
// 常量二元运算
llvm::Constant *SumConst = llvm::ConstantExpr::getAdd(
llvm::ConstantInt::get(Int32Ty, 5),
llvm::ConstantInt::get(Int32Ty, 7)
);
指令(llvm::Instruction 及子类)
llvm::Instruction 继承自 User,表示一条 IR 指令。它也是 Value 的子类,因此可作为其他指令的操作数。所有具体的 IR 指令(如 add、load、call、br 等)都派生自 Instruction。
操作数访问与修改
- 获取操作数数目
llvm::Instruction *I = /* 某条指令 */; unsigned NumOps = I->getNumOperands();
- 访问/设置操作数
llvm::Value *Op0 = I->getOperand(0); I->setOperand(1, NewValue);
- 遍历所有操作数
for (unsigned i = 0; i < I->getNumOperands(); ++i) { llvm::Value *Op = I->getOperand(i); // 处理操作数 Op }
删除与替换
- 将指令从基本块中移除并返回下一个迭代器
auto NextIt = I->eraseFromParent(); // 注意:eraseFromParent 会销毁指令 I 并返回下一个指令的迭代器
- 用另一个值替换
I->replaceAllUsesWith(NewValue); // 将 I 的所有使用替换为 NewValue,但 I 本身仍在基本块中,需手动删除 I->eraseFromParent(); // 删除指令 I
常见指令示例
以下列出部分常见指令的创建与用法示例。
二元运算(llvm::BinaryOperator)
#include "llvm/IR/Instructions.h"
// 在基本块 BB 的末尾插入一条 add 指令:%sum = add i32 %lhs, %rhs
llvm::Value *LHS = /* 某个 Value* */;
llvm::Value *RHS = /* 某个 Value* */;
llvm::BinaryOperator *AddInst = llvm::BinaryOperator::Create(
llvm::Instruction::Add, // Op 类型
LHS, // 左操作数
RHS, // 右操作数
"sum", // 指令名称(可选)
BB // 插入位置:基本块末尾
);
// 其他算数运算:Sub, Mul, UDiv, SDiv, SRem, FRem, ...
比较指令(llvm::CmpInst) 整数比较(ICmp)
#include "llvm/IR/Instructions.h"
// 创建一条整数比较:%cmp = icmp sgt i32 %a, %b
llvm::CmpInst *ICmpInst = llvm::CmpInst::Create(
llvm::Instruction::ICmp, // Op 类型
llvm::CmpInst::ICMP_SGT, // Predicate
LHS, // 操作数1
RHS, // 操作数2
"cmp", // 名称
BB // 插入位置
);
浮点比较(FCmp)
// %fcmp = fcmp olt double %x, %y
llvm::CmpInst *FCmpInst = llvm::CmpInst::Create(
llvm::Instruction::FCmp,
llvm::CmpInst::FCMP_OLT,
X,
Y,
"fcmp",
BB
);
内存操作(alloca、load、store) alloca 指令(在函数的入口块或栈帧中分配内存)
#include "llvm/IR/Instructions.h"
llvm::AllocaInst *AllocaInst = new llvm::AllocaInst(
Int32Ty, // 分配的类型
0, // 地址空间(默认为0)
"stackVar", // 名称
/*插入位置*/ &F->getEntryBlock() // 一般插入到函数入口块
);
// 分配数组:%arr = alloca [10 x i32]
llvm::Value *ArraySize = llvm::ConstantInt::get(Int32Ty, 10);
llvm::AllocaInst *AllocaArr = new llvm::AllocaInst(
Int32Ty, // 数组元素类型
0, // 地址空间
ArraySize, // 数组大小(元素数量)
"arr",
&F->getEntryBlock()
);
getelementptr 指令
#include "llvm/IR/Instructions.h"
// 对指针 ptr 进行 GEP 运算:获取 &ptr[idx]
llvm::Value *IdxConst = llvm::ConstantInt::get(Int32Ty, 5);
llvm::GetElementPtrInst *GEPInst = llvm::GetElementPtrInst::Create(
/*PointeeType=*/Int32Ty,
/*Ptr=*/AllocaInst,
/*IdxList=*/{ IdxConst },
/*Name=*/"gep",
/*BB=*/BB
);
load 指令
llvm::LoadInst *LoadInst = new llvm::LoadInst(
Int32Ty, // 加载的类型
AllocaInst, // 指向的地址
"ld",
BB
);
store 指令
llvm::StoreInst *StoreInst = new llvm::StoreInst(
/*Val=*/llvm::ConstantInt::get(Int32Ty, 123),
/*Ptr=*/AllocaInst,
/*BB=*/BB
);
PHI 节点(llvm::PHINode)
PHINode 必须出现在基本块的最顶端,用于合并来自不同前驱基本块的值。
#include "llvm/IR/Instructions.h"
// 假设 BB 有两个前驱:Pred1 和 Pred2,且它们分别生成值 V1 和 V2
llvm::PHINode *Phi = llvm::PHINode::Create(
Int32Ty, // 类型
2, // 预留两个 incoming
"phi",
BB // 插入位置:BB 的开头
);
// 设置 incoming 列表
Phi->addIncoming(V1, Pred1);
Phi->addIncoming(V2, Pred2);
// 或者后续动态添加
// Phi->addIncoming(NewV, NewPredBB);
访问/修改已有的 incoming
unsigned NumIncoming = Phi->getNumIncomingValues();
llvm::Value *IncVal = Phi->getIncomingValue(0);
llvm::BasicBlock *IncBB = Phi->getIncomingBlock(0);
Phi->setIncomingValue(1, SomeValue);
Phi->setIncomingBlock(1, SomeBasicBlock);
注意 PHI 节点必须集中出现在基本块顶端。如果在基本块中间插入 PHI,将报错 “PHI nodes not grouped at top of basic block!”。
函数调用(llvm::CallInst)
#include "llvm/IR/Instructions.h"
// 假设 f 是一个 llvm::Function*,接受两个 i32,返回 i32
llvm::Function *f = /* 已有函数 */;
llvm::Value *Arg0 = llvm::ConstantInt::get(Int32Ty, 10);
llvm::Value *Arg1 = llvm::ConstantInt::get(Int32Ty, 20);
llvm::CallInst *CallI = llvm::CallInst::Create(
f->getFunctionType(),
f, // 函数指针
{ Arg0, Arg1 }, // 参数列表
"call_res", // 名称
BB // 插入位置
);
// 设置调用约定(可选)
CallI->setCallingConv(llvm::CallingConv::C);
在 LLVM 11 及更高版本中,可直接使用静态 Create() 重载:
llvm::CallInst *CallI2 = llvm::CallInst::Create(
/*Callee=*/f,
/*Args=*/{ Arg0, Arg1 },
/*Name=*/"callRes2",
/*InsertAtEnd=*/BB
);
分支指令(llvm::BranchInst)
- 无条件分支
// 构造一条跳转到 IfTrueBB 的无条件分支 llvm::BranchInst *BrUncond = llvm::BranchInst::Create( /*IfTrue=*/IfTrueBB, /*InsertAtEnd=*/BB );
- 有条件分支
// cond 是 i1 类型的条件值 llvm::Value *Cond = /* 某个 i1 值 */; llvm::BranchInst *BrCond = llvm::BranchInst::Create( /*IfTrue=*/ThenBB, /*IfFalse=*/ElseBB, /*Cond=*/Cond, /*InsertAtEnd=*/BB );
- 修改目标
BrCond->setSuccessor(0, NewThenBB); BrCond->setSuccessor(1, NewElseBB);
Switch 指令(llvm::SwitchInst)
#include "llvm/IR/Instructions.h"
// value 是 i32 或其他整数类型的值,defaultBB 是默认跳转目标
llvm::Value *Value = /* i32 值 */;
llvm::BasicBlock *DefaultBB = /* BasicBlock* */;
llvm::SwitchInst *SwitchI = llvm::SwitchInst::Create(
/*Value=*/Value,
/*Default=*/DefaultBB,
/*NumCases=*/3, // 预留3个 case
/*InsertAtEnd=*/BB
);
// 添加 case: 当 Value 等于 10 时跳转到 CaseBB1
auto *CaseVal1 = llvm::ConstantInt::get(Int32Ty, 10);
SwitchI->addCase(CaseVal1, CaseBB1);
// 遍历所有 case(包括 default)
for (auto CI = SwitchI->case_begin(), CE = SwitchI->case_end(); CI != CE; ++CI) {
llvm::ConstantInt *OnVal = CI->getCaseValue();
llvm::BasicBlock *DestBB = CI->getCaseSuccessor();
// 处理 case
}
// 获取 default case
auto *DefaultCI = SwitchI->getDefaultDest(); // 切换到默认目标
返回指令(llvm::ReturnInst)
#include "llvm/IR/Instructions.h"
// 返回 void
llvm::ReturnInst *RetVoid = llvm::ReturnInst::Create(
Context,
/*RetVal=*/nullptr,
/*InsertAtEnd=*/BB
);
// 返回 int 值
llvm::Value *RetVal = llvm::ConstantInt::get(Int32Ty, 0);
llvm::ReturnInst *RetValInst = llvm::ReturnInst::Create(
Context,
RetVal,
BB
);
其他常见指令(UnaryOps、CastOps 等)
- 位移与逻辑运算:Shl、LShr、AShr、And、Or、Xor 均由 BinaryOperator::Create 创建。
- 一元运算:Neg 等通过相应静态方法或折叠成 BinaryOperator。
- Cast 指令 ```cpp llvm::Value IntVal = / i32 值 */; // zext i32 -> i64 llvm::Value *ZExt = llvm::CastInst::CreateZExtOrBitCast( IntVal, Int64Ty, “zext”, BB );
// trunc i64 -> i32 llvm::Value Trunc = llvm::CastInst::CreateTruncOrBitCast( /Val=/SomeInt64Val, /DestTy=/Int32Ty, /Name=/”trunc”, /InsertAtEnd=*/BB );
- GetElementPtr(GEP):参见上文。
- Select 指令
```cpp
// %res = select i1 %cond, i32 %val1, i32 %val2
llvm::Value *Cond = /* i1 */;
llvm::Value *ValTrue = /* i32 */;
llvm::Value *ValFalse = /* i32 */;
llvm::SelectInst *SelectI = llvm::SelectInst::Create(
Cond,
ValTrue,
ValFalse,
"selectRes",
BB
);
IRBuilder(llvm::IRBuilder<>)
llvm::IRBuilder<> 是构建 LLVM IR 指令的工具类,封装了常见指令的创建,并自动管理插入位置。使用时只需指定当前插入点,调用对应方法即可。
#include "llvm/IR/IRBuilder.h"
llvm::IRBuilder<> Builder(Context);
// 设置插入点到基本块 BB 的末尾
Builder.SetInsertPoint(BB);
// 生成二元运算:%sum = add i32 %a, %b
llvm::Value *A = /* i32 */;
llvm::Value *B = /* i32 */;
llvm::Value *Sum = Builder.CreateAdd(A, B, "sum");
// 生成 Alloca
llvm::AllocaInst *Alloca = Builder.CreateAlloca(Int32Ty, nullptr, "localVar");
// 生成 Store
Builder.CreateStore(llvm::ConstantInt::get(Int32Ty, 42), Alloca);
// 生成 Load
llvm::Value *LoadVal = Builder.CreateLoad(Int32Ty, Alloca, "ld");
// 生成 Branch
Builder.CreateCondBr(Builder.CreateICmpSGT(LoadVal, llvm::ConstantInt::get(Int32Ty, 0)), ThenBB, ElseBB);
// 生成 Return
Builder.CreateRet(llvm::ConstantInt::get(Int32Ty, 0));
常用方法(仅列举部分):
- CreateAdd, CreateSub, CreateMul, CreateUDiv, CreateSDiv, CreateFAdd 等算数运算
- CreateICmpEQ, CreateICmpSGT, CreateFCmpOLT 等比较
- CreateAlloca, CreateLoad, CreateStore 等内存操作
- CreateBr, CreateCondBr, CreateSwitch 等分支
- CreatePHI, CreateCall, CreateBitCast, CreateTrunc, CreateZExt 等
- CreateGetElementPtr
IRBuilder 会自动推断类型并创建合适的 Constant,例如:
auto *Const5 = Builder.getInt32(5);
元数据与调试信息(Metadata、llvm::DIBuilder)
LLVM IR 支持附加 元数据(Metadata),可用于调试信息、目标属性、循环信息等。常见调试信息通过 llvm::DIBuilder 构造。
普通元数据
- 附加到指令
// 假设 I 是一条 Instruction* llvm::LLVMContext &Ctx = I->getContext(); llvm::MDNode *MD = llvm::MDNode::get( Ctx, { llvm::MDString::get(Ctx, "my_meta"), llvm::ConstantAsMetadata::get(SomeConst) } ); I->setMetadata("my.metadata", MD);
- 获取元数据
if (auto *MD = I->getMetadata("my.metadata")) { // 处理 MDNode* }
调试信息(DIBuilder)
使用 llvm::DIBuilder 在 IR 中添加调试符号,用于生成 DWARF 信息,便于在调试器中单步调试。
#include "llvm/IR/DIBuilder.h"
llvm::DIBuilder DIB(*ModulePtr);
// 创建编译单元
auto *CU = DIB.createCompileUnit(
llvm::dwarf::DW_LANG_C_plus_plus, // 语言
DIB.createFile("example.cpp", "/path/to"), // 源文件
"MyCompiler", // 编译器生产者
false, // 是否优化
"", // 编译选项
0 // 运行时版本
);
// 创建文件与目录
auto *File = DIB.createFile("example.cpp", "/path/to");
// 创建调试类型信息(例如:int)
auto *DIBasicInt = DIB.createBasicType("int", 32, llvm::dwarf::DW_ATE_signed);
// 在函数前创建子程序类型
auto *SPTy = DIB.createSubroutineType(DIB.getOrCreateTypeArray({DIBasicInt, DIBasicInt}));
// 创建调试子程序(Function)
auto *DIFunction = DIB.createFunction(
CU, // 所属编译单元
"addTwoInts", // 函数名称
"addTwoInts", // 链接名称
File, // 文件
10, // 行号
SPTY, // 函数类型
false, // isLocalToUnit
true, // isDefinition
10, // 行号
0, // flags
llvm::DINode::FlagZero // flags2
);
// 在函数入口创建插入点
llvm::DIScope *DScope = DIFunction;
llvm::DILocation *DILoc = llvm::DILocation::get(Context, 10, 1, DScope);
// 生成调试描述的分配
llvm::AllocaInst *AllocaX = Builder.CreateAlloca(Int32Ty, nullptr, "x");
llvm::DILocalVariable *DIVarX = DIB.createAutoVariable(
DScope,
"x",
File,
11,
DIBasicInt
);
DIB.insertDeclare(
AllocaX,
DIVarX,
DIB.createExpression(),
DILoc,
Builder.GetInsertBlock()
);
// 完成 DIBuilder 构建
DIB.finalize();
注意:调试信息构建较为繁琐,需确保生成的 IR 包含正确的 !dbg 元数据链接。
验证与打印 IR
验证模块与函数
利用 llvm::Verifier 对模块或函数 Integrity 进行验证,发现 IR 中潜在的问题。
#include "llvm/IR/Verifier.h"
// 验证整个模块
if (llvm::verifyModule(*ModulePtr, &llvm::errs())) {
llvm::errs() << "模块验证失败!\n";
}
// 验证某个函数
for (auto &F : *ModulePtr) {
if (llvm::verifyFunction(F, &llvm::errs())) {
llvm::errs() << "函数 " << F.getName() << " 验证失败!\n";
}
}
打印 IR
- 打印到标准输出
ModulePtr->print(llvm::outs(), nullptr);
- 打印到文件
std::error_code EC; llvm::raw_fd_ostream OS("output.ll", EC, llvm::sys::fs::OF_None); ModulePtr->print(OS, nullptr); OS.flush();
- 以 Bitcode 格式写入
#include "llvm/Bitcode/BitcodeWriter.h"
std::error_code EC2;
llvm::raw_fd_ostream BCOS("output.bc", EC2, llvm::sys::fs::OF_None);
llvm::WriteBitcodeToFile(*ModulePtr, BCOS);
BCOS.flush();
Module 操作(链接、写入 Bitcode 等)
链接多个模块
使用 llvm::Linker 将若干模块合并成一个。需包含 llvm/Linker/Linker.h。
#include "llvm/Linker/Linker.h"
// 假设有模块 ModA, ModB,想要把 ModB 链接进 ModA
llvm::Linker TheLinker(*ModA);
if (TheLinker.linkInModule(std::move(ModB))) {
llvm::errs() << "模块链接失败!\n";
}
写入与读取 Bitcode
- 写入 Bitcode
llvm::WriteBitcodeToFile(*ModulePtr, BCOS);
- 读取 Bitcode
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Support/MemoryBuffer.h"
llvm::Expected<std::unique_ptr<llvm::Module>> ModOrErr =
llvm::parseBitcodeFile(llvm::MemoryBufferRef(Buf->getMemBufferRef()), Context);
if (!ModOrErr) {
llvm::errs() << "读取 bitcode 错误: " << llvm::toString(ModOrErr.takeError()) << "\n";
} else {
auto &LoadedMod = *ModOrErr;
}
小结
- llvm::Module:表示一个完整的 IR 单元,如何创建、遍历、验证与打印。
- llvm::LLVMContext:表示 LLVM 上下文,存放唯一化表等数据。
- llvm::Type 及其子类:如何获取各种基础类型、指针类型、数组/向量类型、函数类型和结构体类型。
- llvm::Value:所有 IR 值的基类,如何获取类型、名称,遍历使用者并替换。
- llvm::GlobalVariable 与 llvm::GlobalAlias:模块级别的全局符号与别名操作。
- llvm::Function:函数的创建、属性设置、遍历基本块与参数。
- llvm::BasicBlock:创建、前驱/后继遍历以及在函数间移动。
- llvm::Constant 及其子类:整型常量、浮点常量、常量表达式的构造与使用。
- llvm::Instruction 及常见指令:二元运算、比较、alloca、load、store、GEP、PHI、Call、Branch、Switch、Return、Cast、Select 等。
- llvm::IRBuilder<>:高效生成 IR 指令的工具,封装创建指令并自动管理插入位置。
- 元数据与调试信息:普通元数据节点与使用 llvm::DIBuilder 构建 DWARF 调试信息。
- 验证与打印 IR:使用 Verifier 验证模块/函数,打印到标准输出或文件;Bitcode 的写入与读取。
- 模块链接:如何使用 llvm::Linker 将多个模块合并。