本页目录

GCC 与 Clang 内部

覆盖: 编译流水线 (frontend/middle-end/backend) → IR (GIMPLE/LLVM IR) → LTO → PGO → sanitizers → 编译器优化一览 适用: GCC 12+, Clang 15+, 用户态编译

编译流水线

flowchart TD
    SRC["📄 C 源码"]

    SRC --> FRONTEND["🔍 Frontend (语言相关)<br/>预处理 + 词法分析 + 语法分析 + 语义分析"]

    FRONTEND --> AST["🌳 抽象语法树 (AST)"]

    AST --> MIDDLE["⚙️ Middle-end (语言无关, 架构无关)<br/>GIMPLE (GCC) 或 LLVM IR (Clang)<br/>→ 优化遍: 死代码删除, 循环优化, 向量化, ..."]

    MIDDLE --> BACKEND["🔧 Backend (架构相关)<br/>寄存器分配 + 指令选择 + 指令调度"]

    BACKEND --> ASM["📝 目标汇编 (.s)"]

    ASM --> ASSEMBLER["🔗 Assembler"]
    ASSEMBLER --> OBJ["📦 目标文件 (.o)"]

    classDef src fill:#e3f2fd,stroke:#1565c0
    classDef step fill:#f3e5f5,stroke:#7b1fa2
    classDef ir fill:#fff3e0,stroke:#ef6c00
    classDef out fill:#e8f5e9,stroke:#2e7d32
    class SRC,OBJ src
    class FRONTEND,BACKEND,ASSEMBLER step
    class MIDDLE ir
    class AST,ASM out

GCC GIMPLE vs LLVM IR

GCC GIMPLE (GNU Compiler Collection):
  3-地址码形式 → ~200 个优化遍
  格式: 函数 → basic blocks → GIMPLE statements
  用 -fdump-tree-all 观察各优化遍的输出

LLVM IR (Clang):
  SSA 形式 → 更接近高级语言
  -emit-llvm → 输出 .ll 文本 → 可读
  opt → 单独的优化器工具 (独立于 clang)
  llc → 单独的代码生成器

LTO: Link-Time Optimization

# GCC LTO:
gcc -flto -O2 -c a.c b.c
gcc -flto -O2 -o prog a.o b.o  # 链接时看到整个程序的 IR → 跨文件优化

# Thin LTO (Clang, 更快的增量 LTO):
clang -flto=thin -O2 -c a.c b.c

# LTO 的典型收益:
#   - 内联跨文件的小函数 (成本模型能看成千上万个函数)
#   - 死代码消除 (分析整个程序 → 移除未调用的函数)
#   - 常量传播 (跨文件追踪常量)
#   二进制大小: ↓5-15%, 性能: ↑2-5%

PGO: Profile-Guided Optimization

# 1. Instrument (插桩):
gcc -fprofile-generate -o prog prog.c
./prog  # 运行 → 生成 .gcda 文件 (profile data)

# 2. Recompile (用 profile 指导):
gcc -fprofile-use -O2 -o prog prog.c

# PGO 的典型收益:
#   - 分支预测优化 (热路径上的代码重排)
#   - 内联决策更准 (频繁调用的函数 → 内联)
#   - 寄存器分配更优 (热变量的优先级高)
#   性能: ↑5-15%

Sanitizers: 运行时错误检测

# Address Sanitizer (ASAN): heap out-of-bounds, UAF, stack overflow
gcc -fsanitize=address -g -o prog prog.c

# Undefined Behavior Sanitizer (UBSAN):
gcc -fsanitize=undefined -o prog prog.c
# 检测: 整数溢出, 移位越界, null 解引用, 类型对齐

# Thread Sanitizer (TSAN): data races
gcc -fsanitize=thread -g -o prog prog.c

# Memory Sanitizer (MSAN): uninitialized reads (Clang only)
clang -fsanitize=memory -g -o prog prog.c

# 开销:
#   ASAN: ~2x slow, ~2x mem
#   TSAN: ~5-10x slow, ~5-10x mem
#   UBSAN: ~1.2x slow (最轻量, 生产可用)

常用优化选项

-O0: 无优化 (调试)
-O1: 基础优化 (不增加代码体积)
-O2: 标准优化 (-O1 + 指令调度 + 别名分析 + ...) 
-O3: 激进优化 (-O2 + 向量化 + 内联更激进)
-Os: 优化体积 (-O2 但跳过增加体积的优化)

# 特定优化:
-fomit-frame-pointer  # 不保留帧指针 (多一个寄存器, 略快, 但 debug 麻烦)
-march=native         # 针对本机 CPU 优化 (使用 AVX2/BMI/...)
-ftree-vectorize      # 自动向量化 (SIMD)
-funroll-loops        # 循环展开 (可能增大体积, 可能更快)
-fno-semantic-interposition  # 不生成 PLT 间接 (库内调用更快)

编译产物分析

# 观察优化结果:
gcc -O2 -S -o - prog.c | less     # 汇编输出
gcc -O2 -fdump-tree-all prog.c    # GCC: 所有 GIMPLE 优化遍
clang -O2 -Rpass=loop-vectorize prog.c  # Clang: 优化决策报告

# 二进制分析:
objdump -d prog | less             # 反汇编
nm prog | grep -v '^_'             # 符号表
size prog                          # 各段大小

参考

  • 文档⁠: GCC Internals Manual, LLVM Language Reference
  • LWN: "LTO and PGO", "Sanitizers in GCC and Clang"

关键词: GCC, Clang, GIMPLE, LLVM IR, LTO, ThinLTO, PGO, ASAN, UBSAN, TSAN, optimization flags