如何从GCC源码学编译原理

本文结合编译原理理论和GCC实践做了一个总结,希望能给需要了解编译原理和底层知识的同学一个更快的学习路径。

了解事物的本质,是一件非常愉快的事情。

学习编译原理好处:

编译原理可以说是一个计算机科学的缩影,在学习寄存器分配中会使用到贪心算法,死代码消除中会用到图论算法,数据流分析中使用到Fixed-PointAlgorithm,词法分析和语法分析中会使用到有限状态机和递归下降等重要思想。可见编译原理是值得学习的。

源程序到目标代码的过程要经历如下四个步骤:

首先是源程序到抽象语法树:

需要经历词法分析,也就是将程序中的一个一个字符按照单词识别出来。

然后是语法分析,将词法分析阶段的单词构成短语,将短语以抽象语法树的形式存储起来。

接下来是语义分析,语义分析是审查源程序有无语义错误,为代码生成阶段收集类型信息。

从源程序到抽象语法树的过程称为编译器前端。

目标代码生成:中间代码到目标代码会经过大量的代码优化,例如死代码删除、指令调度等。这个过程称为编译器后端。

编译器后端是整个编译器中最精华的地方,如果想要提升程序性能,研究编译器后端算法绝对会让你受益良多。

下面是一条语句i=j+k*10编译过程的具体实例:

程序=数据结构+算法

相信很多关心程序效率的同学都有这样的体验:

算法和数据结构密不可分,一个高效的算法必然有合适的数据结构作为支撑。高效的算法与合理的数据结构同样重要。

对于算法,通常仔细读一读论文就可以弄清楚原理,但是去看一些开源代码具体实现的时候往往又一头雾水。

那是因为我们刚接触开源代码的时候并不清楚它的数据结构是如何设计的。

本文从GCC的数据结构开始入手,为想要快速上手GCC的同学提供一个捷径。

即使不关心GCC源码,也可以从数据结构设计中获得启发,毕竟合理的数据结构和高效的算法一样重要。

我们通常认为GCC是一个编译器,然而官方的解释是这样的:

GCCisnotacompiler.

GCCisacompilercollectionthatconsistsofthreecomponents.

Afrontendforeachprogramminglanguage,amiddleend,andabackendforeacharchitecture.

也就是说GCC是一个编译器集合,支持多种语言和多种硬件架构。

下图是GCC的一个整体结构图

GCC整体结构图

图中的绿色的部分Generic、GIMPLE、RTL是本文要介绍的,看懂这三个数据结构之后离看懂GCC源码基本就成功了一半。

GCC中的Generic其实也是一种抽象语法树(AST)。

从GCC整体结构图中我们可以看到,在Generic前面已经生成了AST,为啥这儿的Generic也是一种AST呢?

原因是这样的,从GCC整体结构图中我们可以看到,C语言会生成一种AST,C++也会生成一种AST,Java还会生成一种AST。这三种AST其实还是会有一些细微的差别,因此设计了一种通用的AST去统一所有的语言生成的AST,这个通用的AST就是Generic。

有了统一的AST后,就不用针对多种语言的AST写多份code去生成目标代码。只需要针对统一的AST写一份code去生成目标代码。

将每一种节点统一到一个联合体中的好处就是便于代码阅读和代码的编写与维护。

structtree_var_declvar_decl;

两者交叉的部分为变量的类型,因为a与b都是int类型,所以用指针指向同一个int类型节点。

变量a与变量b通过var_decl的chan字段以链的方式连接起来。

在GCC中很多前端处理并不包含AST到GENERIC的转换,而是直接将AST转换成与语言无关的另外一种中间表示,即GIMPLE。

从GCC整体框架图可以看到,AST转换成GIMPLE之后首先进行静态单赋值(SSA),然后进行各种优化pass。

gimplify_function_tree是生成GIMPLE的入口函数。

其作用是通过扫描函数的AST,分别对函数的返回值、函数参数、函数中的变量以及函数体的语句序列进行处理,并将其转换成对应的GIMPLE序列。

gimplify_body函数,对函数的内容进行GIMPLE转换。

gimplify_parameters对函数的参数列表进行GIMPLE转换。

gimplify_stmt,函数体的GIMPLE生成是通过调用gimplify_stmt完成的。

gimplify_expr函数是GIMPLE生成的核心函数,由gimplify_stmt调用。

另外一个需要注意的是,对于带有操作数的GIMPLE语句,这些操作数的节点指针(类型为tree)将被连续存放在从该结构体最后一个成员treeop[1]开始的连续地址中。

RTL中文叫做寄存器传输语言(RegisterTransferLanguage)。RTL是一种非常接近汇编指令的中间表示。RTL采用了类似LISP语言的列表形式,描述了每一条指令的语义动作。

刚接触RTL的时候对其含义并不是太了解,导致代码难理解,因此在这儿对RTL的含义进行简要介绍,方便初学者能够快速入门GCC。

RTL是下面这样子的:

别以为是乱码,刚开始见的时候确实非常奇怪,但弄清楚之后非常简单。

其中set表示等号或者说是赋值,plus表示加法。SI表示寄存器存取的模式,SI表示该寄存器以32位整形的模式存取。

整个RTL的意思是:将寄存器139与寄存器138相加的值赋给寄存器140.

用一张图表示就是:

其中的XEXP(x,0)是GCC源码中用来取第一个操作数的代码。

知道了RTL表示后阅读源码就会轻松许多,当然还有一些细节没有介绍,需要了解的可以后台回复GCC,下载我收集的几份比较好的国外PPT,再配合源码看起来会方便很多。

THE END
1.阅读源码的经验总结如何阅读第三方开源库 选择一些当下热门、学习价值高的第三方开源库,我认为最值得学习的第三方开源库是Volly,开源项目解析中也有对Volly的解析。 热门的第三方开源库,网上会有很多源码解析文章,首先在网上找一些源码解析文章来看看,不要忘记,我们的宗旨之一“高效”,这样就可以快速的对项目的整体框架有一个大体的了解https://www.jianshu.com/p/be86e5678252
2.如何学习源码如何学习优秀的源码如何学习源码 一、程序员的层次划分(本文只针对学习源码部分) 1、只关注项目本身,不懂就baidu一下。 2、除了做好项目,还会阅读和项目有关的技术书籍,看wikipedia。 3、找一些开源项目看看,大量试用第三方框架,还会写写demo。 (要解决什么问题?如何实现的?)https://blog.csdn.net/shengyin714959/article/details/142281091
3.如何较好的学习框架底层源码?都有自己的优势和特点,在国内TP确实用的很多,我想说的是具体学习哪个框架并不重要,我们需要选择一个安安心心的学好他的底层(如果您时间比较充裕的话,全部学习也是可以的,哈哈),因为你会了一个框架之后,其他框架你自学下很快就会上手,因为框架的核心思想基本是相通的,本文章我就拿TP来举例,我们如何学习他的源码呢https://www.imooc.com/article/284124
4.Vue源码学习之响应式是如何实现的vue.js最近接触了vue.js,一度非常好奇vue.js是如何监测数据更新并且重新渲染页面,这篇文章主要给大家介绍了关于Vue源码学习之响应式是如何实现的相关资料,需要的朋友可以参考下+ 目录 GPT4.0+Midjourney绘画+国内大模型 会员永久免费使用!【 如果你想靠AI翻身,你先需要一个靠谱的工具!】 前言 作为前端开发,我们的日常工作https://www.jb51.net/article/225377.htm
5.还不会Vue2.x源码本地调试!一前言由于自己最近准备学习vue的由于自己最近准备学习vue的源码,所以首先我得知道怎么样去本地调试vue得源码。后续也会把学习中遇到得问题、校验梳理成文章,也当作自己的学习笔记。 写在前面,我看的版本是"version":"2.6.14" 二、如何进行本地调试 首先我们找到vue2的源码地址进行本地clone https://juejin.cn/post/7081983962339508261
6.面向学生的.NET学习使用C#编程语言编程学习如何编写 C# 代码 嗨朋友们! 学习编码可能令人生畏。我们将随时为你提供帮助。 开始 获得认证 使用与freeCodeCamp合作进行的新基础 C# 认证,展示你的 C# 知识。该认证是全面的、可全球访问的,最重要的是,它是免费的,可确保世界各地的学习者都能从针对Microsoft Learn的可靠 C# 培训中获益。https://dotnet.microsoft.com/zh-cn/learntocode
7.RocketMQ消息积压,异步方案,缓存策略解决方案如何入手学习源码 最核心的一点就是查看官方文档 官方文档是所有技术中 最权威,最齐全的一个资料聚集地 有些翻译中文的网站,可能会做到更新不及时,所以还是建议直接看英文文档,借助翻译即可。也可以锻炼英文水平 首先要掌握这个技术的整体结构,有哪些功能特性,涉及到的关键技术、实现原理和生态系统等等。掌握了这些,对https://developer.aliyun.com/article/932643
8.idea看python源码mob64ca12f3496a的技术博客在上面的代码中,使用requests库获取API数据时,可以按住Ctrl并点击get函数,将打开get函数的源码,让你更好的了解它的实现逻辑。 第六步:理解和学习源码 在你打开Python源码后,仔细阅读代码及其注释,可以发现大多数库都包含详尽的解释。这会帮助你了解函数的功能与实现。 https://blog.51cto.com/u_16213443/12795867
9.源码时代成都IT培训重庆UIh5Java培训不敢去怎么办?毕业就拿近万元offer!现在的应届生都这么强?在源码毕业后,能进大厂吗?答案来了!狂欢7天7夜!源码时代11周年庆超燃来袭源码时代天府校区正式开课!2021源码时代课程更新、服务升级、就业加速:新价目标准将于4月1日正式执行 源码时代学习环境 硬核设施,舒适的环境,铸就你的美好未来https://www.itsource.cn/
10.人类高质量Java学习路线一条龙版做一个完整的项目的确很不容易,建议大家根据自己的时间、兴趣选择较新的、有配套源码的教程,保持耐心。 如何选择编程学习资源,可以看下我的原创文章:https://mp.weixin.qq.com/s/mlMql9RJCd7THt6rpGb8UA 下面推荐一些优质的、较新的项目实战视频教程 + 50 套项目源码。 https://xie.infoq.cn/article/a9fd4615c281e8ca41840ce37
11.北京大学金芝教授受邀出席云南省金芝专家工作站揭牌仪式并作学术金芝教授作源代码表示学习的报告 揭牌仪式结束后,金芝教授主要围绕源代码表示学习为软件学院师生作科研报告,详细介绍了深度学习技术如何赋能源代码分析工作。金芝教授通过主题突出的精彩报告,围绕代码表示学习发展历程及其重要意义和代码表示学习方法等方面同师生们进行了深入交流和研讨互动。针对师生提问,金芝教授给予了深入详http://www.sei.ynu.edu.cn/info/1056/2111.htm
12.macd指标背离公式源码(学习)博客macd指标背离公式源码(学习) 一、MACD顶底背离副图 DIFF:EMA(CLOSE,12) - EMA(CLOSE,26);DEA:EMA(DIFF,9);MACD:2*(DIFF-DEA),COLORSTICK;A1:=BARSLAST(REF(CROSS(DIFF,DEA),1));B1:=REF(C,A1 1)>C AND REF(DIFF,A1 1)A2:=BARSLAST(REF(CROSS("KDJ.K"(9,3,3),"KDJ.D"(9,3,3)),1))http://blog.eastmoney.com/yudi010/blog_220210785.html
13.助记词盗u秒u源码转地址转私钥可直接对接假钱包app端源码仅供学习交流使用,一切法律后果均与本站无关。 助记词转地址 + 余额更新 + 一键账号余额一键归集 大于多少u自动转走 可直接对接假钱包,具体方法已经写在教程里了 教程 本系统集合助记词转地址 + 余额更新 + 一键余额一键归集 安装教程php7.2 mysql5.6 php安装gmp扩展 安装nodehttps://www.jianshu.com/p/https://yiqucode.com/qukuailian-3696.html
14.Git--distributed-even-if-your-workflow-isnt Git is afree and open sourcedistributed version control system designed to handle everything from small to very large projects with speed and efficiency. Git iseasy to learnand has atiny footprint with lightning fast performance. It outclasses SCM tools https://git-scm.com/