gcc源码分析与应用系列教程(2)进入编译器前端

gcc编译器,从4.5版本开始,编写语言采用c++,但是无论是c语言还是c++语言,主函数是不变的,都是main,我们先找到其中的main函数。
非常直观,main函数的源码文件就叫main.c,我们来看一下它的内容:
gcc源码分析与应用系列教程(2)进入编译器前端
我们看到,在main函数里面,首先创建一个toplev对象,然后调用toplev对象的main方法进入到程序中,我们继续顺着找到toplev的定义,在gcc目录下,能找到toplev.h和toplev.c。
gcc源码分析与应用系列教程(2)进入编译器前端
在toplev的构造函数中,只是初始化了一些属性,我们先跳过不看,重点来看里面的main方法。
toplev::main这个函数由于内容比较多,这里只讲一些重点的流程,一些加了注释的内容,就不再重复的翻译一边。
首先是初始化一些配置:
gcc源码分析与应用系列教程(2)进入编译器前端
这里面,我们需要重点注意一个名为lang_hooks的变量。
在上一章我们说过,gcc不在是一个纯粹的c语言编译器,而是支持多个编程语言,每一个编程语言都有独立的词法,语法和语义,为此gcc专门设计了一个名为lang_hooks这样的全局变量来管理这些编程语言的前端。
很不幸,lang_hooks的类型定义也叫lang_hooks:
gcc源码分析与应用系列教程(2)进入编译器前端
我们可以在gcc目录下寻找这几个文件,langhooks.h,langhooks.c,langhooks_def.h。
在介绍这三个文件之前,我们先来看一下lang_hooks的数据结构,由于lang_hooks比较庞大,注释也比较齐全,要看完整的可以直接到langhooks.h里面去找。
gcc源码分析与应用系列教程(2)进入编译器前端
尽管gcc被用c++重写过,但是很多地方依然采用c语言,我们可以看到lang_hooks里面大量使用了指针函数,而没有使用c++的虚函数机制。
那么这样写就有个问题,如何实现多态?
在gcc里面,用了一个很巧妙的方法,那就是使用宏定义来实现接口与继承,我们来看langhooks_def.h。
gcc源码分析与应用系列教程(2)进入编译器前端
它首先定义一个宏,将一个默认函数和这个宏绑定,而这个默认函数就定义在langhooks.c文件里面,默认函数是个空函数,什么都不做。
如果用户重写了这个指针函数,就使用#undef取消定义后,再#define重新和这个宏绑定,后面我们会遇到这样的例子。
在看过lang_hooks后,我们回到toplev::main函数继续往下看,会找到一个名为do_compile函数,这个函数就像它的名字一样直截了当。
到这里,我们就开始进入到编译环节,开始编译用户输入的源码,下面我们先假设我们输入的源码时c语言,继续跟进到do_compile函数里面,再跟进到compile_file函数。
然后再compile_file函数中,我们找到一个lang_hooks.parse_file函数,通过这个函数,我们进入到c语言的前端,以下是一些截图,这些截图都是toplev.c文件里面的,帮助大家方便找到相关的位置:
gcc源码分析与应用系列教程(2)进入编译器前端gcc源码分析与应用系列教程(2)进入编译器前端
gcc源码分析与应用系列教程(2)进入编译器前端
下面,我们进入到c语言的前端实现中去:
gcc源码分析与应用系列教程(2)进入编译器前端
我们记得,进入c语言是调用lang_hooks.parse_file函数,那么根据这个线索,我们首先要找到这个函数再哪里被绑定上去的,找到c-objc-common.h:

gcc源码分析与应用系列教程(2)进入编译器前端
然后在clang.c文件里面,会对lang_hooks这个全局变量进行初始化:
gcc源码分析与应用系列教程(2)进入编译器前端
在本章最后阶段,我们来介绍一下LANG_HOOK_INITIALIZER,它的原型定义在langhooks_def.h文件里面,在最后行:
gcc源码分析与应用系列教程(2)进入编译器前端
本章我们顺着main函数的流程,成功进入了gcc编译器c语言的前端模块中,下一张我们将具体分析前端的实现,如果有小伙伴还行了解其他细节,欢迎给我私信哦。