GCC进行编译的大概步骤:
词法分析 --> 语法分析 --> 生成语法树 --> 高级gimple --> 低级gimple --> cfg --> ssa -->RTL -->目标代码(汇编代码)
在gcc实际的编译过程中,词法分析是在语法分析的驱动下进行的,也即是语法分析在什么时候需要下一个符号,就在词法分析识别下一个符号
函数声明的作用是,函数定义未出现时,一旦这个函数被调用,可以通过声明来确定函数信息,所以他是一种辅助信息,不参与执行,所以,不需要转换为中间代码
main函数转变为高端gimple的过程:
1 以函数为单位进行转化,并且将函数内部的所有变量以及编译器为方便生成运行时结构所创建的临时变量都提高到函数最开始的位置,为计算栈空间和使用寄存器提供依据
2 将函数执行语句集中到一起,并且其顺序与语法树种所表现的顺序一致,为配合运行时结构会增减一些语句
gimple语句:
1 ## 赋值语句用gimple_assign来表示,该节点主要内容的含义如下:
gimple_assign
subcode : 操作码
num_ops:操作数的个数,可以为2 (一元表达式),3 (二元表达式),4(三元表达式)
op[0]: 操作数0
op[1]: 操作数1
op[2]:操作数2
2 ## 函数调用语句 gimple_call
gimple_assign
num_ops(unsigned int)[5]:操作数的个数,可以为2 (一元表达式),3 (二元表达式),4(三元表达式)
op[0]: 函数返回值 m.0
op[1]: 被调用函数 fun
op[3]:第一个参数 i
op[4]:第二个参数 j
3 ## 函数返回值语句
gimple_return
num_ops[1]
op[0]:变量D.1380
"return D.1380;"
语法树到高端gimple的转化是以语法树的节点为单位进行遍历的
高端gimple到低端gimple主要完成数据合并,代码合并和返回语句合并,有利于最后生成更规整的后端代码
主要是将高端gimple结构中的所有gimple_bind节点拆开,所有gimple_bind变量都放到一起,并记录在local_decls中,
为讲了运行时结构的函数开栈空间提供依据,所有的gimple_bind中的语句也都放到一起,并记录在gimple_body中,将来转化成汇编指令
return语句转低端gimple的处理:
1 在gimple_return语句的位置插入一条goto lable跳转语句
2 将gimple_return语句暂存起来,将gimple语句序列中的gimple_return语句删掉
3 待所有的语句都lower gimple转换完之后,再将gimple_return语句做gimple_return的处理
,处理过程是先添加一个标号,以便于第一句的goto lable对应上,然后再把return 语句插入gimple语句序列
其实经过处理得到的低端gimple已经足以支持生成最终的目标代码,确定运行时结构了,但是gcc考虑到优化,
在此基础上转化了cfg和ssa结构,考虑到平台的通用性,又生成了一套通用的RTL结构,将在RTL的基础上转化为目标代码
#### 低端gimple到cfg结构
GCC设计cfg ( control flow graph ) 主要是用于函数内部的控制流转化,跨函数间的逻辑优化由于逻辑比较复杂,GCC目前还没有完成
cfg的主要作用是在低端gimple的基础上将语句分成几个基本块(basic block),在基本块内,代码是顺序执行的,不存在跳转语句,如果有跳转语句,则放在块的最后,保证跳转只发生在块与块之间,即在gimple中,指令跳转的语句就是基本块的边界
### cfg转ssa
为每个变量增加一个版本号,用于数据流的优化,它的结构是跟低端gimple的结构相同的
### 生成RTL
由于GCC是支持多种平台的,在不同的平台上生成的汇编代码的格式肯定是不同的,如果为每个平台的汇编代码都写一套优化逻辑,是不太现实的,为了解决这个问题,GCC提供了一种中间形式的汇编语言RTL(Register Transfer Language),它与具体的平台无关,这样所有的优化都可以基于RTL了,在所有的优化完成之后,再转变成针对不同硬件平台的汇编代码,每一条RTL语句称为一条insn语句
insn语句一共有6类:
insn ## 一般指令
jump_insn ## 跳转指令
call_insn ## 函数调用指令
code_lable ## 标号,用来表示跳转目标
barrier ## 放在insn语句的序列中,用来表示控制流中不可能到达的位置,eg在无条件跳转的后边
note ## 编译指导信息,例如调试和声明信息等
#### 转化为RTL阶段的主要步骤
1 转化为初始的RTL
2 明确初始的RTL中的运行时结构信息,此时把虚拟寄存器更新为真实的寄存器
#### RTL生成目标代码
也就是汇编代码,就是我们所熟悉的了