生成 LLVM IR(二)

核心知识点讲解

1. 控制流指令生成

LLVM IR 中的控制流主要通过分支指令(br)和跳转指令实现。分支指令有两种形式:

  1. 条件分支br i1 %cond, label %true_block, label %false_block
  2. 无条件分支br label %target_block

在 C++ API 中,创建基本块和分支指令的步骤如下:

  1. 创建函数的入口基本块
  2. 创建其他需要的基本块
  3. 在基本块之间添加分支指令

在 Python API 中,流程类似,但语法更简洁。

2. 内存访问指令生成

LLVM IR 提供了丰富的内存访问指令:

  1. 分配内存alloca 指令在栈上分配内存
  2. 加载内存load 指令从内存加载值
  3. 存储内存store 指令将值存储到内存
  4. 获取地址getelementptr 指令(GEP)用于计算复合类型的地址

GEP 指令是 LLVM 中最强大的地址计算指令,它可以:

  • 计算数组元素的地址
  • 计算结构体字段的地址
  • 支持链式地址计算

3. 函数调用指令生成

函数调用在 LLVM IR 中通过 call 指令实现。调用函数需要:

  1. 函数的类型签名
  2. 函数的引用
  3. 传递给函数的参数

函数调用的语法:%result = call %function_type @function_name(%arg1, %arg2, ...)

4. 全局变量和常量

LLVM IR 支持全局变量和常量:

  1. 全局变量:使用 @ 前缀,如 @global_var
  2. 常量:使用 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 实现:

  1. 控制流:通过分支指令(br)和基本块实现条件语句和循环
  2. 内存访问:使用 allocaloadstoregetelementptr 指令
  3. 函数调用:通过 call 指令调用函数并传递参数
  4. 复杂逻辑:结合多种指令实现计算器等复杂功能

LLVM IR 提供了丰富的指令集和灵活的表示方式,使得编译器开发者能够方便地生成高效的中间代码。通过掌握这些技术,我们可以为不同的源代码构造生成对应的 LLVM IR,为后续的代码优化和目标代码生成奠定基础。

在下一集中,我们将开始中间代码生成的实战部分,从 AST 到三地址码的转换过程。

« 上一篇 生成 LLVM IR(一) 下一篇 » 中间代码生成实战(一)—— 表达式