LLVM 编译器基础设施
LLVM 编译器基础设施
LLVM (Low Level Virtual Machine)
LLVM 是一个模块化和可重用的编译器和工具链技术集合。它提供了现代化的编译器基础设施,支持静态和动态编译,广泛应用于各种编程语言和硬件平台。
📋 章节概览
🎯 学习目标
通过本章节的学习,你将掌握:
- LLVM 架构: 理解 LLVM 的整体架构和设计原则
- LLVM IR: 熟练读写 LLVM 中间表示
- Pass 开发: 编写自定义的分析和变换 Pass
- 后端开发: 了解目标代码生成的基本原理
- 优化技术: 掌握常见的编译器优化方法
🏗️ LLVM 架构概览
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Frontend │ │ Optimizer │ │ Backend │
│ │ │ │ │ │
│ C/C++ → IR │───▶│ IR → IR (Opts) │───▶│ IR → Machine │
│ Rust → IR │ │ │ │ Code │
│ Swift → IR │ │ Pass Manager │ │ │
│ ... → IR │ │ │ │ x86/ARM/GPU/... │
└─────────────────┘ └──────────────────┘ └─────────────────┘
🔧 环境搭建
从源码构建 LLVM
# 克隆 LLVM 项目
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
# 创建构建目录
mkdir build && cd build
# 配置构建
cmake -G Ninja ../llvm \
-DLLVM_ENABLE_PROJECTS="clang;lld" \
-DLLVM_TARGETS_TO_BUILD="X86;ARM;AArch64;NVPTX;AMDGPU" \
-DCMAKE_BUILD_TYPE=Release \
-DLLVM_ENABLE_ASSERTIONS=ON
# 编译
ninja
验证安装
# 检查版本
./bin/llvm-config --version
./bin/clang --version
# 运行测试
ninja check-llvm
📝 第一个 LLVM IR 程序
C 源码
// hello.c
#include <stdio.h>
int main() {
printf("Hello, LLVM!\n");
return 0;
}
生成 LLVM IR
# 生成 LLVM IR
clang -S -emit-llvm hello.c -o hello.ll
# 查看生成的 IR
cat hello.ll
LLVM IR 示例
; hello.ll
@.str = private unnamed_addr constant [13 x i8] c"Hello, LLVM!\00"
declare i32 @printf(i8*, ...)
define i32 @main() {
entry:
%call = call i32 (i8*, ...) @printf(i8* getelementptr inbounds
([13 x i8], [13 x i8]* @.str, i32 0, i32 0))
ret i32 0
}
🚀 核心组件
1. LLVM Core
- Module: 编译单元的顶层容器
- Function: 函数定义和声明
- BasicBlock: 基本块,控制流的基本单位
- Instruction: 指令,计算的基本单位
2. Pass Manager
- ModulePass: 模块级别的 Pass
- FunctionPass: 函数级别的 Pass
- BasicBlockPass: 基本块级别的 Pass
- LoopPass: 循环级别的 Pass
3. 代码生成器
- SelectionDAG: 指令选择的中间表示
- MachineFunction: 机器级别的函数表示
- Register Allocation: 寄存器分配
- Instruction Scheduling: 指令调度
🎨 优化示例
死代码消除 (DCE)
; 优化前
define i32 @example() {
%1 = add i32 1, 2 ; 死代码
%2 = add i32 3, 4
ret i32 %2
}
; 优化后
define i32 @example() {
ret i32 7 ; 常量折叠 + DCE
}
循环优化
; 循环展开前
for.body:
%i = phi i32 [ 0, %entry ], [ %inc, %for.body ]
%sum = phi i32 [ 0, %entry ], [ %add, %for.body ]
%add = add i32 %sum, %i
%inc = add i32 %i, 1
%cmp = icmp slt i32 %inc, 4
br i1 %cmp, label %for.body, label %for.end