热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

GCC编译流程及中间RTL的探索

文章标题:GCC编译流程及中间RTL的探索。Linux是中国IT实验室的一个技术频道。包含桌面应用,Linux系统管理,内核研究,嵌入式系统和开源等一些基本分类

  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.c 文件中只有一个函数 main,而这个 main 函数中也只有一条语句,调用了一下toplev_main 函数。之所以单独用一个 main 函数来调用 toplev_main,是为了让不同的语言前端可以方便设计不同的 main 函数。
  
  toplev_main 函数是在 toplev.c 文件中定义的,从名字中就可以看出这个文件应该是用来控制 gcc 最顶层的编译流程的,在程序开始的注释中也说明了它是用来处理命令行参数、打开文件、以合适的顺序调用各个分析程序 [5] 并记录它们各自所用的处理时间。toplev_main 首先对 gcc 做了一下初始化,主要是设置环境变量和诊断信息等等,然后就开始解析命令行参数,我们对这些并不感兴趣,重要的是接下来调用了 do_compile 函数,这个函数看从名字看就是做编译工作的,而在此之后 toplev_main 函数就返回了。
  
  do_compile 函数也是在 tolev.c 中定义的,它调用了一些函数来做进一步的初始化,比如对编译过程中计时器的初始化、针对特定程序设计语言的初始化以及对后端的初始化等等,同时它还对 toplev_main 函数中解析的命令行参数做了进一步处理。在完成了上述工作后,调用了 compile_file() 函数,这个函数应该是用来进行真正的编译工作了。
  
  compile_file 函数还是在 toplev.c 中定义的,这里提一下 compile_file 函数和上面的do_compile 函数,它们是参数和返回类型都为 void 的函数,在编译的时候需要的各种参数包括编译的文件名、编译参数以及 gcc 内部使用的一些钩子函数等等都是采用全局变量来表示的,当然,这些全局变量在前面各种初始化函数中都已经被适当地初始化了。接着说compile_file 函数,它又做了一些我们并不太关心的初始化工作,之后,它终于调用了一个钩子函数来分析(parse)整个输入文件了:
  
  (*lang_hooks.parse_file)(set_yydebug);
  
  
  这里的 lang_hooks 是一个全局变量,不同语言的前端对此赋以不同的值,以便调用各自特有的分析程序,关于 lang_hooks 结构的定义和初始化等等可以参见源码中的 langhooks.h、langhooks.c 和 langhooks-def.h 等文件,这里就不详细追究了。对于 C 语言来说,这条语句相当于调用了 c-opts.c 中的 c_common_parse_file 函数。
  
  c_common_parse_file中调用了c-parse.c中的c_parse_file函数,在此函数中又调用了同样位于c-parse.c中的yyparse函数。有必要介绍一下c-parse.c文件,它是由GNU bison [6] 从c-parse.y中得到的一个语法解析器。c-parse.y则是一个YACC文件,它使用BNF(Backus Naur Form)来描述了某种程序设计语言的语法。 [7]
  
  至此,我们对gcc中主要的函数调用关系还是相当清楚的,从main函数层层深入,进入了c-parse.c中的yyparse函数。前面提到过c-parse.c文件是由GNU bison对c-parse.y这个YACC文件作用后自动生成的,这导致这段代码阅读起来比较困难,因为bison生成的c-parse.c文件中有很多条goto语句以及超过500个case的switch语句,如此多的选择和跳转语句无疑给追踪gcc的函数调用带来了极大的困难,我们不可能再继续下去了。
  
  再回过头去看看前面那些代码和注释以及一些文档,注意到多次提到过一个函数??rest_of_compilation,这似乎是一个很重要的函数,我们可以过去看看。
  
  
  在toplev.c中我们找到了这个函数,注释中说明它的作用是:在对程序中顶层的函数定义或者变量的定义处理以后,接着对这些函数或者变量进行编译并输出相应的汇编代码,在此函数返回后,gcc内部使用的tree结构就消亡了。看来这个函数的功能比较复杂,它已经把源程序对应的汇编代码生成了,并且把对应的tree结构占用的空间已经释放了,而我们所感兴趣的部分是gcc编译过程中内部使用RTL表示的情况,这部分处理应该是在rest_of_compilation这个函数返回之前做的。
  
  前面我们从main函数跟踪到了yyparse函数,这里又发现了一个很重要的rest_of_compilation函数,但中间这段过程gcc做了些什么我们还不清楚,也许我们所关心的有关RTL的处理就在其中。
  
  现在我们只有对gcc进行调试才能确切的看清进入yyparse后函数调用的情况了,这里介绍一下调试gcc的方法:
  
  对gcc进行调试,其实是对编译gcc源代码所得到的cc1程序调试,进入到cc1所在的目录,运行命令:
  
  
  $ gdb cc1
  $ break main
  $ run -dr /PATH/test.c
  
  
  这样就是以-dr为编译参数运行gcc来编译test.c文件了,并且在main函数的入口处设置了一个断点,-dr作为编译参数就是要求在RTL表示生成以后将其dump到一个以.rtl结尾的文件中去。接下来在rest_of_compilation之前再设置一个断点,并用continue命令运行到该断点,用backtrace命令查看此时函数栈帧的情况:
  
  $ break rest_of_compilation
  $ continue
  $ backtrace
  
  下表1给出了使用gdb调试时显示出的从main到rest_of_compilation的函数调用情况:
  
 

  
表1. 部分函数调用栈帧列表

  
  调试的结果证实我们前面的分析是正确的,从main函数到yyparse函数的调用顺序与我们阅读代码时所分析得到的结果是吻合的。现在我们得到了gcc编译时从yypare到rest_of_compilation之间的一系列函数调用,这些都是值得关注的目标,让我们返回到源码中去看看这些函数的功能。
  
  时刻记得我们的目标:对于gcc如何生成tree结构我们并不关心,也不关心gcc是如何由中间表示层RTL生成汇编代码的,我们感兴趣的是RTL表示是如何生成的,并希望在RTL表示层做一些修改,以达到我们的目的。为了省去一些篇幅,本文中略去了对那些我们不太关心的函数的分析,直接跳转到RTL生成和处理相关的部分。
  
  终于,在tree-optimize.c中的tree_rest_of_compilation中,我们发现了一系列看起来是与RTL生成有关的函数调用,特别引起我们注意的又是一个钩子函数:
  
  (*lang_hooks.rtl_expand.stmt) (DECL_SAVED_TREE (fndecl));
  
  
  这行代码
推荐阅读
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 本文介绍了brain的意思、读音、翻译、用法、发音、词组、同反义词等内容,以及脑新东方在线英语词典的相关信息。还包括了brain的词汇搭配、形容词和名词的用法,以及与brain相关的短语和词组。此外,还介绍了与brain相关的医学术语和智囊团等相关内容。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Echarts图表重复加载、axis重复多次请求问题解决记录
    文章目录1.需求描述2.问题描述正常状态:问题状态:3.解决方法1.需求描述使用Echats实现了一个中国地图:通过选择查询周期&#x ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • Python字典推导式及循环列表生成字典方法
    本文介绍了Python中使用字典推导式和循环列表生成字典的方法,包括通过循环列表生成相应的字典,并给出了执行结果。详细讲解了代码实现过程。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • “你永远都不知道明天和‘公司的意外’哪个先来。”疫情期间,这是我们最战战兢兢的心情。但是显然,有些人体会不了。这份行业数据,让笔者“柠檬” ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 生成对抗式网络GAN及其衍生CGAN、DCGAN、WGAN、LSGAN、BEGAN介绍
    一、GAN原理介绍学习GAN的第一篇论文当然由是IanGoodfellow于2014年发表的GenerativeAdversarialNetworks(论文下载链接arxiv:[h ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
author-avatar
晓玲建雯东佳
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有