生成 LLVM IR(二)
核心知识点讲解
1. 控制流指令生成
LLVM IR 中的控制流主要通过分支指令(br)和跳转指令实现。分支指令有两种形式:
- 条件分支:
br i1 %cond, label %true_block, label %false_block - 无条件分支:
br label %target_block
在 C++ API 中,创建基本块和分支指令的步骤如下:
- 创建函数的入口基本块
- 创建其他需要的基本块
- 在基本块之间添加分支指令
在 Python API 中,流程类似,但语法更简洁。
2. 内存访问指令生成
LLVM IR 提供了丰富的内存访问指令:
- 分配内存:
alloca指令在栈上分配内存 - 加载内存:
load指令从内存加载值 - 存储内存:
store指令将值存储到内存 - 获取地址:
getelementptr指令(GEP)用于计算复合类型的地址
GEP 指令是 LLVM 中最强大的地址计算指令,它可以:
- 计算数组元素的地址
- 计算结构体字段的地址
- 支持链式地址计算
3. 函数调用指令生成
函数调用在 LLVM IR 中通过 call 指令实现。调用函数需要:
- 函数的类型签名
- 函数的引用
- 传递给函数的参数
函数调用的语法:%result = call %function_type @function_name(%arg1, %arg2, ...)
4. 全局变量和常量
LLVM IR 支持全局变量和常量:
- 全局变量:使用
@前缀,如@global_var - 常量:使用
constant关键字,如@constant_value = constant i32 42
全局变量和常量在模块级别声明,函数内部可以访问它们。
实用案例分析
案例 1:实现条件语句(if-else)
C++ API 实现
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/LLVMContext.h>
using namespace llvm;
int main() {
LLVMContext context;
Module module("if_else_example", context);
IRBuilder<> builder(context);
// 创建函数类型:int main()
FunctionType *funcType = FunctionType::get(builder.getInt32Ty(), false);
Function *mainFunc = Function::Create(funcType, Function::ExternalLinkage, "main", module);
// 创建基本块
BasicBlock *entryBlock = BasicBlock::Create(context, "entry", mainFunc);
BasicBlock *ifBlock = BasicBlock::Create(context, "if_true", mainFunc);
BasicBlock *elseBlock = BasicBlock::Create(context, "if_false", mainFunc);
BasicBlock *endBlock = BasicBlock::Create(context, "if_end", mainFunc);
builder.SetInsertPoint(entryBlock);
// 创建条件:i32 1 > i32 0
Value *one = builder.getInt32(1);
Value *zero = builder.getInt32(0);
Value *cond = builder.CreateICmpSGT(one, zero, "cond");
// 条件分支
builder.CreateCondBr(cond, ifBlock, elseBlock);
builder.SetInsertPoint(ifBlock);
// if 分支:返回 1
builder.CreateBr(endBlock);
builder.SetInsertPoint(elseBlock);
// else 分支:返回 0
builder.CreateBr(endBlock);
builder.SetInsertPoint(endBlock);
// 从 phi 节点获取返回值
PHINode *phi = builder.CreatePHI(builder.getInt32Ty(), 2, "result");
phi->addIncoming(builder.getInt32(1), ifBlock);
phi->addIncoming(builder.getInt32(0), elseBlock);
// 返回结果
builder.CreateRet(phi);
// 打印模块
module.print(outs(), nullptr);
return 0;
}Python API 实现
from llvm import ir
# 创建模块和函数
module = ir.Module(name="if_else_example")
func_type = ir.FunctionType(ir.IntType(32), [])
main_func = ir.Function(module, func_type, name="main")
# 创建基本块
entry_block = main_func.append_basic_block(name="entry")
if_block = main_func.append_basic_block(name="if_true")
else_block = main_func.append_basic_block(name="if_false")
end_block = main_func.append_basic_block(name="if_end")
# 构建入口块
builder = ir.IRBuilder(entry_block)
one = ir.Constant(ir.IntType(32), 1)
zero = ir.Constant(ir.IntType(32), 0)
cond = builder.icmp_sgt(one, zero, name="cond")
builder.cbranch(cond, if_block, else_block)
# 构建 if 块
builder.position_at_end(if_block)
builder.branch(end_block)
# 构建 else 块
builder.position_at_end(else_block)
builder.branch(end_block)
# 构建结束块
builder.position_at_end(end_block)
phi = builder.phi(ir.IntType(32), name="result")
phi.add_incoming(ir.Constant(ir.IntType(32), 1), if_block)
phi.add_incoming(ir.Constant(ir.IntType(32), 0), else_block)
builder.ret(phi)
# 打印模块
print(module)案例 2:实现内存访问
C++ API 实现
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/LLVMContext.h>
using namespace llvm;
int main() {
LLVMContext context;
Module module("memory_access_example", context);
IRBuilder<> builder(context);
// 创建函数类型:int main()
FunctionType *funcType = FunctionType::get(builder.getInt32Ty(), false);
Function *mainFunc = Function::Create(funcType, Function::ExternalLinkage, "main", module);
// 创建基本块
BasicBlock *entryBlock = BasicBlock::Create(context, "entry", mainFunc);
builder.SetInsertPoint(entryBlock);
// 在栈上分配内存
AllocaInst *alloca = builder.CreateAlloca(builder.getInt32Ty(), nullptr, "variable");
// 存储值到内存
Value *fortyTwo = builder.getInt32(42);
builder.CreateStore(fortyTwo, alloca);
// 从内存加载值
Value *loadedValue = builder.CreateLoad(builder.getInt32Ty(), alloca, "loaded");
// 返回加载的值
builder.CreateRet(loadedValue);
// 打印模块
module.print(outs(), nullptr);
return 0;
}Python API 实现
from llvm import ir
# 创建模块和函数
module = ir.Module(name="memory_access_example")
func_type = ir.FunctionType(ir.IntType(32), [])
main_func = ir.Function(module, func_type, name="main")
# 创建基本块
entry_block = main_func.append_basic_block(name="entry")
builder = ir.IRBuilder(entry_block)
# 在栈上分配内存
alloca = builder.alloca(ir.IntType(32), name="variable")
# 存储值到内存
forty_two = ir.Constant(ir.IntType(32), 42)
builder.store(forty_two, alloca)
# 从内存加载值
loaded_value = builder.load(alloca, name="loaded")
# 返回加载的值
builder.ret(loaded_value)
# 打印模块
print(module)案例 3:实现函数调用
C++ API 实现
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/LLVMContext.h>
using namespace llvm;
int main() {
LLVMContext context;
Module module("function_call_example", context);
IRBuilder<> builder(context);
// 创建被调用函数:int add(int, int)
FunctionType *addType = FunctionType::get(builder.getInt32Ty(),
{builder.getInt32Ty(), builder.getInt32Ty()}, false);
Function *addFunc = Function::Create(addType, Function::ExternalLinkage, "add", module);
// 创建函数体
BasicBlock *addEntry = BasicBlock::Create(context, "entry", addFunc);
builder.SetInsertPoint(addEntry);
// 获取函数参数
auto argIt = addFunc->arg_begin();
Value *arg1 = &*argIt++;
Value *arg2 = &*argIt;
// 计算参数和并返回
Value *sum = builder.CreateAdd(arg1, arg2, "sum");
builder.CreateRet(sum);
// 创建主函数:int main()
FunctionType *mainType = FunctionType::get(builder.getInt32Ty(), false);
Function *mainFunc = Function::Create(mainType, Function::ExternalLinkage, "main", module);
// 创建主函数体
BasicBlock *mainEntry = BasicBlock::Create(context, "entry", mainFunc);
builder.SetInsertPoint(mainEntry);
// 准备参数
Value *a = builder.getInt32(10);
Value *b = builder.getInt32(20);
// 调用 add 函数
Value *result = builder.CreateCall(addType, addFunc, {a, b}, "call_result");
// 返回结果
builder.CreateRet(result);
// 打印模块
module.print(outs(), nullptr);
return 0;
}Python API 实现
from llvm import ir
# 创建模块
module = ir.Module(name="function_call_example")
# 创建被调用函数:int add(int, int)
add_func_type = ir.FunctionType(ir.IntType(32), [ir.IntType(32), ir.IntType(32)])
add_func = ir.Function(module, add_func_type, name="add")
# 创建函数体
add_entry = add_func.append_basic_block(name="entry")
builder = ir.IRBuilder(add_entry)
# 获取函数参数
arg1, arg2 = add_func.args
# 计算参数和并返回
sum_val = builder.add(arg1, arg2, name="sum")
builder.ret(sum_val)
# 创建主函数:int main()
main_func_type = ir.FunctionType(ir.IntType(32), [])
main_func = ir.Function(module, main_func_type, name="main")
# 创建主函数体
main_entry = main_func.append_basic_block(name="entry")
builder.position_at_end(main_entry)
# 准备参数
a = ir.Constant(ir.IntType(32), 10)
b = ir.Constant(ir.IntType(32), 20)
# 调用 add 函数
result = builder.call(add_func, [a, b], name="call_result")
# 返回结果
builder.ret(result)
# 打印模块
print(module)实用案例分析
案例:实现一个简单的计算器函数
我们将实现一个计算器函数,支持加减乘除四种运算,并处理除零错误。
Python API 实现
from llvm import ir
# 创建模块
module = ir.Module(name="calculator_example")
# 创建计算器函数:int calculate(int, int, int)
# 参数:opcode (0:+, 1:-, 2:*, 3:/), a, b
calc_func_type = ir.FunctionType(ir.IntType(32),
[ir.IntType(32), ir.IntType(32), ir.IntType(32)])
calc_func = ir.Function(module, calc_func_type, name="calculate")
# 创建基本块
entry_block = calc_func.append_basic_block(name="entry")
add_block = calc_func.append_basic_block(name="add")
sub_block = calc_func.append_basic_block(name="sub")
mul_block = calc_func.append_basic_block(name="mul")
div_block = calc_func.append_basic_block(name="div")
div_zero_block = calc_func.append_basic_block(name="div_zero")
end_block = calc_func.append_basic_block(name="end")
# 构建入口块
builder = ir.IRBuilder(entry_block)
opcode, a, b = calc_func.args
# 切换 opcode
switch = builder.switch(opcode, default=end_block)
switch.add_case(ir.Constant(ir.IntType(32), 0), add_block)
switch.add_case(ir.Constant(ir.IntType(32), 1), sub_block)
switch.add_case(ir.Constant(ir.IntType(32), 2), mul_block)
switch.add_case(ir.Constant(ir.IntType(32), 3), div_block)
# 构建加法块
builder.position_at_end(add_block)
add_result = builder.add(a, b, name="add_result")
builder.branch(end_block)
# 构建减法块
builder.position_at_end(sub_block)
sub_result = builder.sub(a, b, name="sub_result")
builder.branch(end_block)
# 构建乘法块
builder.position_at_end(mul_block)
mul_result = builder.mul(a, b, name="mul_result")
builder.branch(end_block)
# 构建除法块
builder.position_at_end(div_block)
zero = ir.Constant(ir.IntType(32), 0)
div_cond = builder.icmp_eq(b, zero, name="div_cond")
builder.cbranch(div_cond, div_zero_block, end_block)
# 构建除零错误块
builder.position_at_end(div_zero_block)
div_zero_result = ir.Constant(ir.IntType(32), -1) # 错误码
builder.branch(end_block)
# 构建结束块
builder.position_at_end(end_block)
result = builder.phi(ir.IntType(32), name="result")
result.add_incoming(ir.Constant(ir.IntType(32), 0), entry_block) # 默认值
result.add_incoming(add_result, add_block)
result.add_incoming(sub_result, sub_block)
result.add_incoming(mul_result, mul_block)
result.add_incoming(builder.sdiv(a, b, name="div_result"), div_block)
result.add_incoming(div_zero_result, div_zero_block)
builder.ret(result)
# 创建主函数测试
test_func_type = ir.FunctionType(ir.IntType(32), [])
test_func = ir.Function(module, test_func_type, name="main")
test_entry = test_func.append_basic_block(name="entry")
builder.position_at_end(test_entry)
# 测试加法
test1 = builder.call(calc_func,
[ir.Constant(ir.IntType(32), 0),
ir.Constant(ir.IntType(32), 10),
ir.Constant(ir.IntType(32), 20)],
name="test_add")
# 测试乘法
test2 = builder.call(calc_func,
[ir.Constant(ir.IntType(32), 2),
ir.Constant(ir.IntType(32), 5),
ir.Constant(ir.IntType(32), 8)],
name="test_mul")
# 测试除法
test3 = builder.call(calc_func,
[ir.Constant(ir.IntType(32), 3),
ir.Constant(ir.IntType(32), 20),
ir.Constant(ir.IntType(32), 4)],
name="test_div")
# 测试除零
test4 = builder.call(calc_func,
[ir.Constant(ir.IntType(32), 3),
ir.Constant(ir.IntType(32), 10),
ir.Constant(ir.IntType(32), 0)],
name="test_div_zero")
# 返回最后一个测试结果
builder.ret(test4)
# 打印模块
print(module)总结
本集我们深入探讨了如何使用 LLVM IR 实现:
- 控制流:通过分支指令(
br)和基本块实现条件语句和循环 - 内存访问:使用
alloca、load、store和getelementptr指令 - 函数调用:通过
call指令调用函数并传递参数 - 复杂逻辑:结合多种指令实现计算器等复杂功能
LLVM IR 提供了丰富的指令集和灵活的表示方式,使得编译器开发者能够方便地生成高效的中间代码。通过掌握这些技术,我们可以为不同的源代码构造生成对应的 LLVM IR,为后续的代码优化和目标代码生成奠定基础。
在下一集中,我们将开始中间代码生成的实战部分,从 AST 到三地址码的转换过程。