
目录
-
- [1. GCC vs LLVM/Clang ------ 对比表](#1. GCC vs LLVM/Clang —— 对比表)
-
- [GCC vs LLVM/Clang ------ 对比简表](#GCC vs LLVM/Clang —— 对比简表)
- [2. `compiler` 术语差异](#2.
compiler术语差异) - [3. 工程结构](#3. 工程结构)
- [4. GCC 真实流水线](#4. GCC 真实流水线)
-
- [1️⃣ 预处理(cpp)](#1️⃣ 预处理(cpp))
- [2️⃣ 编译(前端 + 优化 + 后端)](#2️⃣ 编译(前端 + 优化 + 后端))
- [3️⃣ 汇编(as)](#3️⃣ 汇编(as))
- [4️⃣ 链接(ld)](#4️⃣ 链接(ld))
- [5️⃣ 最终产物](#5️⃣ 最终产物)
- [5. LLVM / Clang 真实流水线](#5. LLVM / Clang 真实流水线)
- [6. GCC vs LLVM 流水线并排对照](#6. GCC vs LLVM 流水线并排对照)
- [7. 一个"现实工程"的关键认知](#7. 一个“现实工程”的关键认知)
-
- [为什么 **你平时只敲一行命令**?](#为什么 你平时只敲一行命令?)
1. GCC vs LLVM/Clang ------ 对比表
| 阶段 | GCC 工具 | GCC 生成文件 | LLVM 工具 | LLVM 生成文件 | 说明 |
|---|---|---|---|---|---|
| 预处理 | cpp(gcc 驱动) |
*.i |
clang -E |
*.i |
宏与头文件展开 |
| 前端解析 | cc1 |
(AST,内部) | clang |
(AST,内部) | 语法 / 语义分析 |
| IR 生成 | cc1 |
(GIMPLE/RTL,内部) | clang -emit-llvm |
*.ll / *.bc |
LLVM IR 可落盘 |
| IR 汇编 | 无此阶段 | --- | llvm-as |
*.bc |
.ll → .bc(可选) |
| IR 链接 | 无此阶段 | --- | llvm-link |
combined.bc |
多 IR 合并(可选) |
| IR 优化 | cc1 |
(内部 IR) | opt |
*.opt.ll / *.bc |
Pass 管线 |
| 后端代码生成 | cc1 |
*.s |
llc |
*.s |
目标相关汇编 |
| 汇编 | as |
*.o |
llvm-mc / as |
*.o |
ELF relocatable |
| 静态库打包 | ar / gcc-ar |
lib*.a |
llvm-ar / llvm-lib |
lib*.a |
归档目标文件 |
| 链接 | ld(gcc 调用) |
app |
lld / ld |
app |
生成可执行文件 |
| 启动文件参与 | ld |
crt1.o crti.o crtn.o |
lld |
crt*.o |
文件被链接,不是工具 |
| 运行库参与 | ld |
libc.so libgcc.a |
lld |
libc.so compiler-rt |
库文件 |
GCC vs LLVM/Clang ------ 对比简表
GCC:
.c → .i → (GIMPLE/RTL) → .s → .o → app
(黑箱)
LLVM:
.c → .i → .ll/.bc → opt → .s → .o → app
(文件级接口)
2. compiler 术语差异
GCC 文档里的 "compiler"
在 GCC 语境中:
compiler = gcc 这个驱动程序 + 内部子程序
文档默认假设:
- 用户调用的是
gcc - 所有阶段都是 gcc 帮你协调
- 内部细节不是 API,而是"实现"
📌 cc1 / cc1plus 在文档里是"实现细节"
LLVM 文档里的 "compiler"
在 LLVM 语境中:
compiler = 一组可以自由组合的工具
例如:
- clang 只是前端
- opt 是优化器
- llc 是后端
- lld 是链接器
📌 每一个工具都是"一等公民"
根本差异总结
| 项目 | GCC | LLVM |
|---|---|---|
| compiler 是什么 | 一个"整体" | 一组"模块" |
| 是否强调内部阶段 | 弱 | 强 |
| 是否鼓励拆开用 | 不鼓励 | 强烈鼓励 |
GCC vs LLVM/Clang 的真实流水线 + 实际命令 + 生成文件。
3. 工程结构
text
project/
├── add.h
├── add.c
└── main.c
add.h
c
#ifndef ADD_H
#define ADD_H
int add(int a, int b);
#endif
add.c
c
#include "add.h"
int add(int a, int b) {
return a + b;
}
main.c
c
#include <stdio.h>
#include "add.h"
int main(void) {
printf("%d\n", add(2, 3));
return 0;
}
4. GCC 真实流水线
前提 :x86_64 Linux / macOS
gcc 15.x 行为一致(只是内部 IR 不可见)
1️⃣ 预处理(cpp)
bash
gcc -E add.c > add.i
gcc -E main.c > main.i
生成文件
add.i
main.i
✔️ 宏展开
✔️ #include 被真正展开
❌ 还不是编译
2️⃣ 编译(前端 + 优化 + 后端)
bash
gcc -S add.i -o add.s
gcc -S main.i -o main.s
内部发生了什么(但你看不到文件):
C
→ AST
→ GENERIC
→ GIMPLE(SSA)
→ RTL
→ 汇编
生成文件
add.s
main.s
📌 GCC 的 IR 全部是"内部态"
3️⃣ 汇编(as)
bash
as add.s -o add.o
as main.s -o main.o
(实际是 gcc 调用 as)
生成文件
add.o
main.o
✔️ ELF relocatable object
✔️ 还不能运行
4️⃣ 链接(ld)
bash
gcc main.o add.o -o app
链接时真实参与的东西(重点)
工具:
ld
输入文件:
main.o
add.o
crt1.o crti.o crtn.o
libc.so
libgcc.a
输出:
app
📌 crt*.o 不是工具,是目标文件
📌 libc.so 不是工具,是共享库
5️⃣ 最终产物
bash
./app
# 输出: 5
5. LLVM / Clang 真实流水线
LLVM 的关键不同点 :
IR 是"可见的文件级产物"
1️⃣ 预处理
bash
clang -E add.c > add.i
clang -E main.c > main.i
生成:
add.i
main.i
(与 GCC 几乎一致)
2️⃣ 生成 LLVM IR(关键分歧点)
bash
clang -emit-llvm -S add.i -o add.ll
clang -emit-llvm -S main.i -o main.ll
生成文件
add.ll
main.ll
📌 这是 LLVM 的"核心资产"
3️⃣ IR 编译 & 优化(可选但真实)
bash
opt -O2 add.ll -o add.opt.bc
opt -O2 main.ll -o main.opt.bc
生成:
add.opt.bc
main.opt.bc
4️⃣ IR 链接(可选,但常见于 LTO)
bash
llvm-link add.opt.bc main.opt.bc -o all.bc
生成:
all.bc
📌 GCC 没有这一阶段
5️⃣ LLVM IR → 汇编(后端)
bash
llc all.bc -o all.s
生成:
all.s
6️⃣ 汇编
bash
llvm-mc -filetype=obj all.s -o all.o
或直接:
bash
clang -c all.s -o all.o
生成:
all.o
7️⃣ 链接
bash
clang all.o -o app
实际链接器
- 默认:
lld - 或系统
ld
参与文件:
crt*.o
libc.so
compiler-rt
6. GCC vs LLVM 流水线并排对照
| 阶段 | GCC | LLVM |
|---|---|---|
| 预处理 | cpp → .i |
cpp → .i |
| 中间表示 | 内部(不可见) | .ll / .bc |
| IR 优化 | cc1 内部 | opt |
| IR 链接 | ❌ | llvm-link |
| 后端 | cc1 | llc |
| 汇编 | as | llvm-mc / as |
| 链接 | ld | lld / ld |
7. 一个"现实工程"的关键认知
为什么 你平时只敲一行命令?
bash
gcc main.c add.c -o app
clang main.c add.c -o app
因为:
gcc / clang 都是"driver"
它们只是帮你自动执行了:
预处理 → 编译 → 汇编 → 链接
GCC 把 main.c + add.c 当成"输入",
LLVM 把它们当成"可拆解的数据流"。
- 本节内容已经全部介绍完毕,希望通过这篇文章,大家对
GCC和LLVM/Clang有了更深入的理解和认识。- 感谢各位的阅读和支持,如果觉得这篇文章对你有帮助,请不要吝惜你的点赞和评论 ,这对我们非常重要。再次感谢大家的关注和支持 !点我关注❤️