1. GCC编译流程概览与中间表示初探
任务目标
编写一个简单的C程序,通过GCC的-v选项观察编译驱动实际调用的子程序,并分别生成预处理文件(.i)、汇编文件(.s)、目标文件(.o)以及GIMPLE和RTL中间表示文件,从而建立对GCC四阶段架构与中间表示的直观认识。
环境准备
需要Linux环境并已安装GCC(含gcc、cc1)。验证安装:
gcc --version
which cc1 # 通常位于 /usr/lib/gcc/x86_64-linux-gnu/<版本>/cc1
示例程序
创建文件 hello.c:
#include <stdio.h>
#define GREETING "Hello, GCC\\n"
int add(int a, int b) {
return a + b;
}
int main(void) {
printf(GREETING);
return add(1, 2);
}
完整实现与运行
以下命令展示了从源码到可执行文件的完整编译流程,并生成各阶段中间产物。在终端中依次执行:
# 1. 观察完整编译流程(预处理→编译→汇编→链接)
gcc -v hello.c -o hello 2>&1 | tee build_log.txt
# 2. 仅预处理(展开头文件与宏),生成 hello.i
gcc -E hello.c -o hello.i
# 3. 生成GIMPLE中间表示(编译器前端与中端输出)
gcc -fdump-tree-gimple hello.c -o /dev/null
# 生成文件:hello.c.005t.gimple
# 4. 生成RTL中间表示(中端与后端接口)
gcc -fdump-rtl-all hello.c -o /dev/null
# 生成文件:hello.c.<编号>r.expand(含多个RTL转储)
# 5. 仅编译(生成汇编),生成 hello.s
gcc -S hello.i -o hello.s
# 6. 仅汇编(生成目标文件),生成 hello.o
gcc -c hello.s -o hello.o
# 7. 仅链接(生成可执行文件)
gcc hello.o -o hello
# 8. 运行验证
./hello
echo "退出码: $?"
关键观察点与验证方法
打开 build_log.txt,应能看到类似以下顺序的驱动调用(实际路径因系统而异):
gcc -v hello.c -o hello
...
/usr/lib/gcc/.../cc1 -E -quiet hello.c -o hello.i # 预处理
/usr/lib/gcc/.../cc1 -fpreprocessed hello.i ... # 编译(生成汇编)
as -o hello.o hello.s # 汇编
/usr/lib/gcc/.../collect2 ... # 链接
验证通过条件:
build_log.txt中依次出现cc1(预处理+编译)、as(汇编)、collect2或ld(链接)的调用行,且顺序正确。hello.i中#include <stdio.h>已被展开,GREETING宏被替换为字符串字面量。hello.c.005t.gimple中包含函数add和main的GIMPLE表示,形如:
;; Function add (add, funcdef_no=0)
add (int a, int b)
{
int D.xxxx;
D.xxxx = a + b;
return D.xxxx;
}
hello.c.*r.expand文件中包含RTL指令序列,每行以(insn ...)或(note ...)开头,寄存器以rxx或%eax等形式出现。- 运行
./hello输出Hello, GCC,退出码为3(即add(1,2)的返回值)。
中间表示文件说明
| 文件后缀 | 阶段 | 内容特征 |
|---|---|---|
.i | 预处理后 | 头文件已展开、宏已替换,仍是C源码 |
.gimple | GIMPLE | 三地址码形式,控制流显式化,表达式被拆分为简单语句 |
.expand(RTL) | RTL | 与机器描述相关的低级中间表示,含伪寄存器与指令模式 |
.s | 汇编 | 目标架构的汇编代码 |
.o | 目标文件 | 机器码+符号表,尚未链接 |
常见问题
- 找不到
cc1路径:在build_log.txt中搜索cc1即可获得完整路径,该路径由GCC内部驱动管理,无需手动调用。 - RTL转储文件过多:
-fdump-rtl-all会生成多个编号文件(如.r.expand、.r.vartrack等)。本案例只需关注expand阶段,即RTL生成的最初形式。 - GIMPLE转储编号可能不同:
005t中的数字可能因GCC版本略有差异,使用通配符查看即可:ls hello.c.*.gimple。