(二) GNU GCC 编译器及其编译流程概述_gcc 的编译过程分为那几个阶段,每个阶段都做了些什么

前端

GCC编译器的前端非常强大,经过多年的发展,GCC 编译器前端已经变得相当成熟,它能编译现在计算机界主流的高级编程语言,前端支持多种编程语言,如 C、Fortran、Pascal、Objective-C、Java、Ada、Go等,同样支持这些语言的标准库libstdc++、 libgcj 等。

GCC编译的前端将不同的高级编程语言经过词法分析、语法分析转化为与前端语言无关的统一的中间表示。有了与前端无关的中间表示,GCC的前端将不同的高级编程语言转换成这种中间表示,这就是GCC处理器支持多种编程语言的根本原因。

后端

编译器分为前端和后端,在GCC的组成结构中,后端负责将RTL表示的中间形式进行优化并最终生成对应平台的汇编代码。GCC后端是在RTL的中间形式上进行的,在RTL上做大量的优化,包括机器有关的优化和机器无关的优化。

RTL是从特定机器平台中抽象出的模型表示的,实际上它是一种抽象出来的机器模型。和所有实际存在的平台一样,这个抽象出来的机器模型也有它自己的“指令系统”。机器模型的“指令系统”在GCC中定义为标准指令模板的一系列指令,这些指令是GCC内部使用的。GCC的后端为不同的平台提供了描述这些指令模板的接口文件,这些指令模板在不同平台上的描述将GCC映射到不同的平台上去,这样就可以把GCC的后端移植到不同的平台上去。通过机器描述支持多种体系结构,机器描述文件给出机器各种参数的宏定义和指令集。这使得GCC移植非常方便,针对目前广泛使用的目标机器的体系结构提供了大部分的支持,后端支持的平台有x86、mips、Alpha、ARM、AVR、IA-64、SPARC、PowerPC等30多种平台。

gcc编译器------前后端的纽带:遍(pass)管理器

GCC编译器的前端将高级语言源码经过词法分析、语法分析生成与高级语言无关的低级中间层表示,后端负责将RTL表示的中间形式进行优化并最终生成对应平台的汇编代码。GCC的前端到后端一共用了三种中间表示,GCC用遍管理器将其连成一个整体。GCC在编译和优化中,对编译对象(一般以函数或文件为处理对象)的一次编译处理,称为pass(遍)。GCC的整个编译处理过程组成了pass_list,这个pass_list包含的所有遍就是整个GCC编译时所经过的过程。遍管理器在GCC编译流程中的作用如下图所示:

GCC编译器前端将源语言进行词法分析、语法分析后得到抽象语法树(AST),该过程为GENERIC。GENERIC之后简化为GIMPLE的中间表示,在GIMPLE层上加入控制流程图(CFG),在这个中间表示层上进行一系列的前期优化(例如,过程间优化IPO,静态单一化赋值SSA等),再将GIMPLE转换成RTL中间表示,同样在RTL上进行一系列的优化,最终生成汇编代码,整个过程在图1中用带箭头的实线表示。从GENERIC到GIMPLE再到RTL,在GCC中是用一系列的遍(pass)连起来的(在图1中用带箭头的虚线表示)形成一个 pass_list。

gcc编译器----中间表示

中间表示( intermediate representation, IR) 指编译器对于源程序进行扫描后生成的内部表示,代表源程序的语义和语法结构,编译器的各个阶段都在IR上进行分析或优化变换,因而它对编译器的整体结构、效率和健壮性都有着极大的影响[38]。中间表示对提高编译器的可移植性以及代码生成起到关键作用,在编译器的研究中,应该设计一种结构良好的中间表示,这种中间表示应在适当的抽象层次上,向上能支持多语言的映射,向下能适应多平台转换且易于进行各种优化。

对于现代编译器来说,编译器前端和后端分别指分析输入源语言和生成目标平台汇编代码的两编译阶段,大部分现代编译器在前端和后端之间会有个中间表示层。有了一个设计良好的中间表示,有m种前端编程语言(C\C++、Fortran、Ada、Java等)和n种后端平台(x86、MIPS、Sparc、ARM等)的编译器设计,这样就减少了为了设计m中语言和n个平台从而设计m*n个编译器设计。

中间语言的使用可以大大减低开发各个语言和各个平台所需编译器的工作量。支持多种编程语言和多种后端平台的编译器可以通过使用一种中间表示来实现。总的来说,中间表示层在减低编译器开发方面的开销和复杂度上功不可没。有些中间表示语言的设计是专门为了支持一种特定的语言,例如Jvm的设计仅仅是为了支持java语言,而大多数中间表示语言的设计是用来将不同的前端语言和后端平台连接起来。

GCC编译器有三种中间表示语言,一种AST/GENERIC比较完善的表示了前端语言的信息,一种GIMPLE用来在相对比较高的层次来表示源语言程序。而另外一种rtl用来高度抽象的表示从特定平台抽象出来的机器指令。

一、GENERIC形式与前端的编程语言是相关的,每种前端语言词法语法分析后形成的AST/GENERIC是异构的,GENERIC包含了前端语言所有的信息。

GCC编译器的前端将高级语言源码经过词法分析、语法分析生成GENERIC。GENERIC是一棵抽象语法树(Abstract Syntax Tree,AST),用数据结构中的树(tree)结构表示。GCC编译器的抽象语法树是源程序的一种中间表示形式,比较直观的表示出源程序的语法结构,并含有源程序结构显示所需要的全部静态信息。GCC 格式的 AST 文件是 GCC 编译源程序时产生的,以文本方式记录源程序抽象语法树的文件。

C语言源码和对应的GENERIC表示如下所示:

C语言源码:

int sum(int a,int b)
{
    int c;
    c=a+b;
    return c;
}

GENERIC表示(部分):

GCC的前端将源语言解析生成GENERIC。不同的前端高级编程语言,GCC前端生成的GENERIC形式不尽相同。每种前端语言词法语法分析后形成的AST/GENERIC是异构的,需要转换成一种统一的中间形式进行后续的处理,这种统一的中间表示形式就是GIMPLE形式。
(关于 GIMPLE 命名是从编译器 SIMPLE IL 改造合成而来)

二、中间表示GIMPLE:GIMPLE是一种三地址码的中间表示形式。

GIMPLE中间形式由GENERIC表达式变换而来,与GENERIC相比,主要有如下的转换:

(1)、通过引入临时变量保存中间结果,并将GENERIC表达式拆分成不超过三个操作数的元组(tuples)。

(2)、GENERIC中的控制结构,例如if-else,for,while等也被转换成条件转移。

(3)、词法作用域(lexical scopes)被取消了。

(4)、异常区域(lexical scopes)被转换成一个单独的异常区域树(exception region tree)。

GIMPLE是通过简化GENERIC得来的,这样做的好处通过下面的例子比较来说明。

xxx@localhost$ cat compare_generic_to_gimple.c

int func(int j, int k) 
{
	int i;
	for (i=0;i>=10;i++) 
	{
		j = i + 1;
		 
		k = j + i + 5;
	}
	return k;
}

int func(int j, int k) 
{
	int i;
	for (i=0;i>=10;i++) 
	{
		j = i + 1;
		 
		    k = j + i + 5;
	}
	return k;
}

使用如下命令进行编译,其中-fdump-tree-gimple选项打印出GCC处理的中间过程,包括列出GENERIC的中间形式和GIMPLE中间形式。

xxx@localhost$ paag-linux-gcc -fdump-tree-gimple -S -o compare_generic_to_gimple.s compare_generic_to_gimple.c
 xxx@localhost$ paag-linux-gcc -fdump-tree-gimple -S -o compare_generic_to_gimple.s compare_generic_to_gimple.c

下表分别给出其GENERIC形式和GIMPLE表示,并进行对比分析。

从上图可以看出,GIMPLE相比较GENERIC而言的优势,也就是为什么要进行GENERIC形式到GIMPLE形式的转化,主要有以下几个方面的原因:

(1) GENERIC是树结构表示和存储的,对于分析和处理非常不便。

(2) GENERIC形式与前端的编程语言是相关的,而GIMPLE是与前端编程语言无关的,所有的语言并不能够表示成统一的GNERIC,这就使得GENERIC并不适合做优化

(3) GIMPLE形式从本质上讲就是线性的代码序列,所有的计算表达式都表示成一系列的基本操作。这样可以更方便有效地进行后续的编译优化,优化算法很容易在GIMPLE形式上实现。在GIMPLE的表示中,控制结构都分解为直接或间接跳转语句。

(4) GIMPLE表达式的表现形式更加严谨,除了函数表达式以外,每个表达式的操作数不超过3个。

(5) GIMPLE表达式将GENERIC分解为3地址格式的表达式,用临时变量来存储计算中的中间值。同样在GIMPLE的表示中,GIMPLE节点并不表示其节点的值,类似CON_EXPR或是BIND_EXPR这样的节点如果有值,那么GIMPLE格式的表示会将其值存储在临时变量中。

GIMPLE形式的中间表示由于其与前端无关的特性,将多种前端高级编程语言统一到一种中间表示上来,并且在GIMPLE层上做了部分优化。随后GCC将GIMPLE转换成RTL形式的中间表示。

三、中间表示RTL:RTL叫做“寄存器转移语言”(Register Transfer Language),它是以一种虚拟寄存器(pseudo register)的方式来叙述计算机行为的语言。RTL 是一种接近机器指令的语言,既有指令序列组成的内部形式,又有机器描述和调试信息组成的文本形式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值