Clang编译全过程为:0输入→1预处理→2编译成IR→3后端生成汇编→4汇编成目标文件→5链接成可执行文件;各阶段输入输出类型明确,阶段间数据流向必须匹配。

用 -ccc-print-phases 看清 Clang 每一步干了什么
Clang 不是黑盒,它把编译拆成明确的阶段。直接运行 clang -ccc-print-phases test.c 就能打印出完整流程图,比如:
+- 0: input, "test.c", c
+- 1: preprocessor, {0}, cpp-output
+- 2: compiler, {1}, ir
+- 3: backend, {2}, assembler
+- 4: assembler, {3}, object
+- 5: linker, {4}, image这表示:从源码开始,依次经过预处理 → 编译成 IR → 后端生成汇编 → 汇编成目标文件 → 链接成可执行文件。每个数字对应一个阶段编号,后面括号里是输入/输出类型。
-E、-S、-c 分别截断到预处理、汇编、目标文件
想单独看某一步的中间产物,不用跑完整流程。常用组合:
-
clang -E test.c:只做预处理,输出宏展开后的test.i(含所有头文件内容) -
clang -S test.c:停在汇编阶段,生成test.s(人类可读的汇编指令) -
clang -c test.c:生成test.o(二进制目标文件,不可直接执行) -
clang -emit-llvm -S test.c:生成test.ll(LLVM IR 文本格式,用于分析优化逻辑)
注意:-S 和 -c 不能同时用;-emit-llvm 必须配合 -S 或 -c 才生效。
为什么 clang --help 里找不到 -ccc-print-phases
这个选项属于 Clang 的“驱动器(driver)”内部调试功能,不在常规帮助列表中。它不参与实际代码生成,只负责展示调度逻辑。常见误操作:
Clang 22.1.3 Windows 64 位历史版本安装包,适合旧项目兼容、LLVM/Clang 工具链回退、编译行为对比、链接问题复现和 C/C++ 构建环境维护。
- 误以为它是编译开关——其实它不改变任何输出文件
- 在 IDE 或构建系统里硬塞进去——多数构建封装会忽略或报错
- 和
-v混用——-v显示调用的底层工具路径,-ccc-print-phases显示阶段拓扑,二者互补但目的不同
真正需要调试编译行为时,优先用 -v 看命令行参数传递,再用 -ccc-print-phases 理清阶段依赖。
不同平台下阶段命名可能略有差异
macOS 上 Clang(来自 Xcode)和 Linux 上 LLVM 官方包对某些阶段的命名不完全一致,比如:
- macOS 输出中可能出现
compiler, {1}, ir,而 Ubuntu 22.04+ 的 clang-14 会标为compiler, {1}, llvm-bc - Windows 下通过 LLVM 安装包运行时,
linker阶段可能被标记为ld或lld,取决于是否启用了-fuse-ld=lld
阶段语义一致,但字符串标签不是跨平台 ABI。别靠字符串匹配写自动化脚本,应以数字序号和输入/输出类型为准。
实际看编译过程时,最容易忽略的是阶段之间的数据流向——比如 .i 文件是 cpp-output,但下一步 compiler 阶段的输入必须是它,否则整个链就断了。很多自定义构建失败,根源在于中间文件类型没对上。

















