新一篇: 解读VC++编程中的文件操作API和CFile类 | 旧一篇: Effective Item21 尽可能使用const
<script>function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}</script>内容摘要 本文将以 C 语言为例,介绍 gcc 在接受一个 .c 文件的输入之后,其前端是如何进行处理并得到一个中间表示并转交给后端处理。然后,在了解了 gcc[1] 的工作流程后,介绍一下作者尝试在 gcc 内部的 RTL 表示层中 hack gcc 的过程,与大家分享一些经验,希望能给对有兴趣研究和开发 gcc 的读者有所帮助。
1. GCC 简介
编译器的工作是将源代码(通常使用高级语言编写)翻译成目标代码(通常是低级的目标代码或者机器语言),在现代编译器的实现中,这个工作一般是分为两个阶段来实现的:
第一阶段,编译器的前端接受输入的源代码,经过词法、语法和语义分析等等得到源程序的某种中间表示方式。
第二阶段,编译器的后端将前端处理生成的中间表示方式进行一些优化,并最终生成在目标机器上可运行的代码。
GCC(GNU Compiler Collection) 是在 UNIX 以及类 UNIX 平台上广泛使用的编译器集合,它能够支持多种语言前端,包括 C, C++, Objective-C, Ada, Fortran, Java 和 treelang 等。
GCC 设计中有两个重要的目标,其中一个是在构建支持不同硬件平台的编译器时,它的代码能够最大程度的被复用,所以 GCC 必须要做到一定程度的硬件无关性;另一个是要生成高质量的可执行代码,这就需要对代码进行集中的优化。为了实现这两个目标,GCC 内部使用了一种硬件平台无关的语言,它能对实际的体系结构做一种抽象,这个中间语言就是 RTL(Register Transfer Language)。
虽然关于 GCC 的研究和开发工作侧重于 GCC 后端代码优化方面,但本文中我们关注的目标是在 GCC 的编译过程中前端是如何工作的。
把 GCC 的前端独立出来研究目的在于,在设计新的编译器的时候,我们仅仅需要关注如何设计新编译器的前端,而将代码优化和目标代码的生成留给 GCC 后端去完成,避免了后端设计的重复性劳动。
本文将以 C 语言为例,介绍 gcc[2] 在接受一个 .c 文件的输入之后,其前端是如何进行处理并得到一个中间表示并转交给后端处理。然后,在了解了 gcc 的工作流程后,介绍一下作者尝试在 gcc 内部的RTL表示层中 hack gcc 的过程,与大家分享一些经验,希望能给对有兴趣研究和开发 gcc 的读者有所帮助。
2. gcc 的工作流程
gcc 是一个驱动程序,它接受并解释命令行参数,根据对命令行参数分析的结果决定下一步动作,gcc 提供了多种选项以达到控制 gcc 编译过程的目的,我们可以在 GCC 的手册中查找这些编译选项的详细信息。
gcc 的使用是比较简单的,但是要深入到其内部去了解编译流程,情况就比较复杂了。面对庞大的[3] gcc,我们只能选择感兴趣的部分来分析。但我们无法获得关于 gcc 编译流程的详尽文档[4] ,这主要是由于 gcc 本身过于繁杂,而且它处于不断的变化当中,所以我们只有通过其它途径来了解 gcc。有两个比较好的方法:一是阅读 source,对感兴趣的函数可以跟踪过去看一看,阅读代码看起来可怕,但其实代码中会有很多注释说明它的功能,使得我们的阅读变得更简单一些,这种方法 便于从整体上把握 gcc;另外一个是 debug gcc,就是使用调试器来跟踪 gcc 的编译过程,这样可以看清 gcc 编译的实际流程,也可以追踪我们感兴趣的细节部分。我们先从大处着眼,从 source 中看看 gcc 一些比较重要的函数以及它们之间的调用关系,然后在 hack gcc 的时候,对 gcc 进行 debug 来追踪我们关心的细节,并且可以通过调试来发现和修改 patch 中的错误。
在开始阅读 gcc 的代码之前,推荐您阅读一下 GCC internals 中 passes and files of the compiler 一章——如果您以前没有看过的话,这段内容会帮助您对 gcc 的结构建立一个大概的映像。
好了,我们以 gcc 中的函数为单位,希望能够尽量详细地描述 gcc 中自顶向下的函数调用关系。在 gcc 源码目录中,很容易就发现了一个文件 main.c,应该是 gcc 的入口了,这个main.