一、LLVM的架构设计

LLVM(Low Level Virtual Machine)的核心设计思想是将编译器分为三个阶段:前端(负责解析和语义分析)、优化器(与语言无关的优化)、后端(目标代码生成)。这种分离设计使得同一套优化和后端可以服务于多种语言。

1.1 前后端分离架构

# LLVM架构
#
# 源代码 ──► [Clang前端] ──► IR ──► [优化器] ──► IR
#                                          │
#                                          ▼
#                                    [LLVM后端] ──► 目标代码
#                                          │
#           ┌──────────────────────────────┘
#           ▼
#     x86 ASM │ ARM ASM │ WASM │ NVPTX │ ...

# LLVM IR(Intermediate Representation):
# 一种类似汇编的SSA(Static Single Assignment)形式
# 既是优化的输入,也是优化的输出

# LLVM IR示例:
; ModuleID = 'test.cpp'
source_filename = "test.cpp"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

define i32 @add(i32 %a, i32 %b) {
entry:
  %result = add nsw i32 %a, %b
  ret i32 %result
}

# 关键特性:
# - SSA形式:每个变量只赋值一次
# - 无限寄存器:虚拟寄存器%0, %1, ...
# - 类型系统:i32(32位整数), float, ptr等

二、Clang前端工作流程

2.1 词法分析到AST构建

# Clang编译流程:
# ① 词法分析(Lexical Analysis)→ Token流
# ② 语法分析(Syntax Analysis)→ AST(抽象语法树)
# ③ 语义分析(Semantic Analysis)→ 类型检查、作用域
# ④ IR生成(IR Generation)→ LLVM IR

# 查看Clang生成的AST(开发调试用):
$ clang -Xclang -ast-dump -fsyntax-only test.cpp

# 输出示例(部分):
# TranslationUnitDecl 0x... <> ...
# |-CXXRecordDecl 0x... struct Foo
# | |-CXXMethodDecl 0x... void foo()
# | | `-CompoundStmt 0x...
# | `-...

# 常用Clang静态分析工具:
# ① scan-build:静态分析整个项目
$ scan-build cmake -B build && scan-build cmake --build build

# ② clang-tidy:现代Lint工具
$ clang-tidy test.cpp -checks='*,-modernize-*' --fix

# ③ clang-format:代码格式化
$ clang-format -i -style=llvm test.cpp

# 集成到CMake:
find_package(LLVM REQUIRED CONFIG)
find_package(Clang REQUIRED CONFIG)
clang_tidy_check_add_target(my_target)

2.2 编译警告与诊断

# Clang警告级别:
# -Werror:将警告视为错误
# -Wall -Wextra:常用警告集合
# -Weverything:全部警告(谨慎使用)

# 生产级编译选项:
# -Wall -Wextra          # 常用警告
# -Wpedantic             # 严格符合ISO C++
# -Wconversion          # 隐式类型转换警告
# -Wnull-dereference     # 空指针解引用
# -Wdouble-promotion     # float→double隐式提升
# -Wformat=2            # printf/scanf格式检查

# 代码:
int main() {
    const char* fmt = "%d";   // 警告:格式字符串不匹配
    printf(fmt, 3.14);        // 打印double但格式是%d
}

# Clang诊断输出:
# warning: format specifies type 'int' but the argument
# has type 'double' [-Wformat]
# printf(fmt, 3.14);
#          ^~~~~
#          (int)3.14

三、LLVM优化器实战

3.1 通过opt查看优化效果

# 使用opt进行LLVM IR级别优化
# test.ll是LLVM IR文件

# 查看可用的优化pass:
opt -O3 -debug-pass=Structure test.ll -o /dev/null 2>&1 | head -30

# 常用优化Pass:
# ① -mem2reg:mem2reg(将alloca提升为寄存器,生成SSA)
# ② -instcombine:代数简化(x*2 → x+x)
# ③ -dce:无用代码消除
# ④ -inline:函数内联
# ⑤ -loop-unroll:循环展开
# ⑥ -gvn:全局值编号(消除冗余计算)

# 对比不同优化级别:
$ clang -O0 -S -emit-llvm test.cpp -o test_O0.ll
$ clang -O3 -S -emit-llvm test.cpp -o test_O3.ll

# 关键优化示例:循环不变代码外提( LICM )
# 原始代码(test.ll):
# for(i=0; i

3.2 Link Time Optimization(LTO)

# LTO:链接时进行跨模块优化
# 传统:各.cpp单独编译为.o,链接时只看符号
# LTO:编译时生成字节码格式(.bc),链接时整体优化

# 启用LTO:
# GCC/Clang:
clang++ -flto -O2 a.cpp b.cpp -o program
# 或分步:
clang++ -flto -c a.cpp -o a.o
clang++ -flto -c b.cpp -o b.o
clang++ -flto a.o b.o -o program

# CMake中启用LTO:
include(CheckIPOSupported)
check_ipo_supported(RESULT LTO_SUPPORTED OUTPUT LTO_ERROR)
if(LTO_SUPPORTED)
    set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()

# LTO优化效果示例:
# 跨模块内联:a.cpp调用b.cpp的small_func()
# 无LTO:无法内联(跨.o边界)
# 有LTO:small_func被内联到a.cpp的调用点
# 性能提升:5-15%(取决于跨模块调用密度)

# ⚠️ LTO的代价:
# 编译时间增加 2-5倍
# 链接时间显著增加
# 调试信息膨胀

四、MLIR:下一代编译器基础设施

# MLIR(Multi-Level Intermediate Representation):
# LLVM的进化,支持多层IR(方言/Dialect)

# 应用场景:
# ① TensorFlow/XLA:用MLIR统一高层优化(循环融合、内存规划)
# ② IREE(LLVM子项目):将PyTorch/TensorFlow统一到LLVM
# ③ 自定义硬件:定义领域特定方言

# MLIR方言层次:
# ┌─────────────────────────────┐
# │  高层方言(TensorFlow Dialect) │  ← Python/PyTorch模型
# │  算子方言(Linalg Dialect)     │  ← 循环嵌套操作
# │  硬件抽象(Arith/Vector/Scf) │  ← 向量化/并行化
# │  底层方言(LLVM Dialect)      │  ← 寄存器/指令
# └─────────────────────────────┘
# 每个方言可独立优化,减少编译复杂度

# 生产建议:
# 普通应用开发:用Clang/LLVM即可
# 编译器研究/ML框架:关注MLIR生态
# 硬件加速器:MLIR + LLVM后端是最好的起点