《大前端开发者需要了解的基础编译原理和语言知识》读后感

这几天看了一篇名为《大前端开发者需要了解的基础编译原理和语言知识》,读完之后深感作者知识面之广,内容之丰富,感谢作者分享如此高质量的文章。

同时,鉴于文章内容较多,自己写下这篇文章,作为心得和笔记。

代码的编译过程往粗了说分为四个阶段:1.预处理(preprocessing)2.编译(compliation)3.汇编(assembly)4.链接(linking)

往细了说分为七个阶段:1.预处理、2.词法分析、3.语法分析、4.生成中间代码、5.生成目标代码、6.汇编、7.链接

这里面的主要区别就是编译包括了词法分析、语法分析、生成中间代码和生成目标代码四个部分。

编译器负责预处理、词法分析、语法分析、生成中间代码和生成目标代码五个步骤,编译器的输入是源代码,输出是中间代码。编译器以中间代码为分界又分为编译器前端编译器后端编译器前端负责语法分析生成抽象语法树(AST,Abstract Syntax Tree)后端负责将抽象语法树转换为中间代码
ps:中间代码已经非常接近于实际的汇编代码,它几乎可以直接被转化。主要的工作量在于兼容各种 CPU 以及填写模板。此工作由编译器或汇编器完成。

主流的C/C++的编译器有GCC,这是GNU发布的一款极具影响力的编译器,已经成为LinuxUnix系统的默认编译器。从事iOS开发使用的Xcode目前用的编译器是clang+LLVM。Xcode4之前,用的是GCC编译器,由于GCC对Objective-C的支持不是很好,于是苹果采用了“自家”发起的clang+LLVM作为默认编译器。

这两个编译器的主要区别是GCC直接负责整个编译过程的五个步骤,而clang+LLVM将编译的步骤拆分,clang作为编译器前端还记得编译器前端的职责么?),LLVM作为编译器的后端两者配合使用。当然历时上也曾经出现过,GCC作为编译器前端LLVM作为编译器后端的搭配组合。

Xcode 3和Xcode 4时代曾经出现的GCC+LLVM搭配

接下来就以细分的模块为例,总结一下每个模块由什么负责,分别都做了哪些工作。

1.预处理(preprocessing) —— 处理宏定义

预处理主要是处理一些宏定义,比如:#define#include、**#if **等。预处理的实现有很多种,有的编译器会在词法分析前先进行预处理,替换掉所有 # 开头的宏,而有的编译器则是在词法分析的过程中进行预处理。当分析到 # 开头的单词时才进行替换。虽然先预处理再词法分析比较符合直觉,但在实际使用中,GCC 使用的却是一边词法分析,一边预处理的方案。(以上这些内容是我从《大前端开发者需要了解的基础编译原理和语言知识》直接粘贴过来的)

2.词法分析 —— 输出符号状态

词法分析的主要实现原理是状态机,它逐个读取字符,然后根据读到的字符的特点转换状态。计算机不像人类可以直接识别源代码的内容,它只能一个一个的识别每个单词,词法分析要做的就是把源代码分割开,形成若干个单词,并标记状态为语法分析做准备。比如,a=1”“a==1”,这两个语句,计算机在从左向右识别时,识别到第一个 “=” 时并不能判定该语句是赋值符号还是条件判断符号,必须结合 “=” 之后的字符才能做出正确的判断,若是 “1” 则是赋值符号,若是 “=” 则是条件判断符号,根据识别的不同结果,进行状态标记。具体的案例在原文中讲的很详细,各位读者可以参考。

词法分析.png

3.语法分析 —— 输出抽象语法树(AST, Abstract Syntax Tree)

词法分析以后,编译器已经知道了每个单词的意思,但这些单词组合起来表示的语法还不清楚,这时需要编译器前端(如:GCC或者clang)进行语法分析

实现语法分析的一个简单的思路是模板匹配,即将编程语言的基本语法规则抽象成模板进行匹配,符合相应的模板,即对应相应的意思,同时生成抽象语法树(AST, Abstract Syntax Tree)。

比如有 “int a = 10;” 语句,其实表示了这么一种通用的语法格式:
类型 变量名 = 常量;”。

而在成功解析语法以后,我们会得到抽象语法树(AST: Abstract Syntax Tree)。

语法分析.png

以这段代码为例:

int fun(int a, int b) {
int c = 0;
c = a + b;

return c;

}

它的语法树如下:

抽象语法树.jpg

语法树将字符串格式的源代码转化为树状的数据结构,更容易被计算机理解和处理。

4.生成中间代码IR(Intermeidate Representation)

其实抽象语法树可以直接转化为目标代码(汇编代码)。然而,不同的 CPU 的汇编语法并不一致,比如《AT&T与Intel汇编风格比较》这篇文章所提到的,Intel 架构AT&T 架构的汇编码中,源操作数目标操作数位置恰好相反。Intel 架构操作数立即数没有前缀但** AT&T 架构有。因此一种比较高效的做法是先生成语言无关,CPU 也无关的中间代码(IR)**,然后再生成对应各个 **CPU **的汇编代码。

生成中间代码(IR,Intermediate Representation)是非常重要的一步,一方面它和语言无关,也和 CPU 与具体实现无关。可以理解为中间代码是一种非常抽象,又非常普适的代码。它客观中立的描述了代码要做的事情,如果用中文、英文来分别表示** C Java 的话,中间码某种意义上可以被理解为世界语**。

另一方面,中间码编译器前端后端的分界线。编译器前端负责把源码转换成中间代码(IR)编译器后端负责把中间码转换成目标代码(汇编代码)
抽象语法树生成中间码(IR)的过程大体上分为两步,第一步是生成中间码(IR),第二步是将中间码(IR)最佳化

抽象语法树转中间码.png

中间码最佳化.png

以** GCC** 为例,生成中间代码可以分为三个步骤:(
1.语法树转高端 gimple
2.高端 gimple 转低端 gimple
3.低端 gimple 经过 cfa 转 ssa 再转中间代码

注:使用Xcode 进行iOS 客户端开发时,LLVM 负责将抽象语法树转为中间码(IR)

** Gimple** 是GCC编译器产生的一种包含最多三个操作数的中间指令,也就是编译原理里讲的四元码(三个操作数,一个操作符),基本上也就是 dst = src1 @ src2 的这种形式。由于** Gimple** 最多只能对两个操作数进行计算,因此一个复杂的表达式会展开为一系列的** Gimple** 指令,这一过程就是** Gimple** 化。

4.1 语法树转高端 Gimple

语法树高端 Gimple,主要是处理寄存器,比如:“ c = a + b” 并没有直接的汇编代码与之对应,一般来说需要把 a + b 的结果保存到寄存器中,然后再把寄存器赋值给 c。另外,调用一个新的函数时会进入到函数自己的栈,建栈的操作也需要在 Gimple 中声明。

4.2 高端 Gimple转低端Gimple

高端 Gimple低端 Gimple,主要是把变量定义语句执行返回语句区分存储,分配栈空间。
比如:

int a = 1;
a++;
int b = 1;

会被处理成:

int a = 1;
int b = 1;
a++;

这样做的好处是很容易计算一个函数到底需要多少栈空间。

此外,return 语句会被统一处理,放在函数的末尾,比如:

if (1 > 0) {
return 1;
}
else {
return 0;

}

会被处理成:

if (1 > 0) {
goto a;
}
else {
goto b;
}
a:
return 1;
b:
return 0;

4.3 低端Gimple转中间代码

这一步主要是进行各种优化,添加版本号等。

5.生成目标代码(汇编代码)

目标代码也可以叫做汇编代码。由于中间码已经非常接近于实际的汇编代码,它几乎可以直接被转化。主要的工作量在于针对不同的CPU生成不同的汇编代码,兼容各种 CPU 以及填写模板。在最终生成的汇编代码中,不仅有汇编命令,也有一些对文件的说明。

生成目标代码.png

6.汇编(assembly)——汇编器生成二进制机器码

汇编器会接收汇编代码,将它转换成二进制机器码,生成目标文件(后缀是 .o),机器码可以直接被 CPU 识别并执行。由于目标代码是分段的,最终的目标文件(机器码)也是分段的。这是因为:

  1. 数据和代码区分开。其中代码只读,数据可写,方便权限管理,避免指令被改写,提高安全性。
  2. 现代 CPU 一般有自己的数据缓存和指令缓存,区分存储有助于提高缓存命中率。
  3. 当多个进程同时运行时,他们的指令可以被共享,这样能节省内存。

7.链接(linking)

链接就是将目标文件(.o文件)与其中调用的外部函数所在的目标文件通过重定位关联起来。

在一个目标文件中,不可能所有变量函数都定义在文件内部。比如** strlen 函数就是一个被调用的外部函数,此时就需要把 main.o 这个目标文件和包含了 strlen 函数实现的目标文件链接起来。我们知道函数调用对应到汇编其实是 jump 指令,后面写上被调用函数的地址,但在生成 main.o 的过程中, strlen() 函数的地址并不知道,所以只能先用 0 来代替,直到最后链接**时,才会修改成真实的地址。

链接器就是靠着重定位表来知道哪些地方需要被重定位的。每个可能存在重定位的段都会有对应的重定位表。在链接阶段链接器会根据重定位表中,需要重定位的内容,去别的目标文件中找到地址并进行重定位

有时候我们还会听到动态链接这个名词,它表示重定位发生在运行时而非编译后。动态链接可以节省内存,但也会带来加载的性能问题,这里不详细解释,感兴趣的读者可以阅读《程序员的自我修养》这本书。(以上是从《大前端开发者需要了解的基础编译原理和语言知识》直接粘贴过来的)

写在最后

最后十分感谢《大前端开发者需要了解的基础编译原理和语言知识》的作者能够贡献如此高质量的一篇文章,讲清楚了整个编译过程中的细节。本文有大量内容是直接从该文章中复制过来,最终的版权归原作者所有,本人只是作为一个读后笔记写下本文,若有侵权的地方,烦请告知。
参考文献

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,050评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,538评论 1 306
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,673评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,622评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,047评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,974评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,129评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,893评论 0 209
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,654评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,828评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,297评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,619评论 3 262
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,326评论 3 243
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,176评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,975评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,118评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,909评论 2 278

推荐阅读更多精彩内容

  • 一、以 Hello World开篇 Hello World对程序员而言肯定是如雷贯耳。但是简单的事物背后往往包含这...
    ZhengYaWei阅读 8,331评论 10 84
  • 前言 2000年,伊利诺伊大学厄巴纳-香槟分校(University of Illinois at Urbana-...
    星光社的戴铭阅读 15,750评论 8 180
  • TITLE: 编程语言乱炖 码农最大的烦恼——编程语言太多。不是我不学习,这世界变化快! 有时候还是蛮怀念十几、二...
    码园老农阅读 5,186评论 2 35
  • 又是一年 乡土难离 灯火烛光 鸟儿纷飞 故乡梦里 推开旧窗 缘来泪水 那些年岁 习惯城市高墙 红叶随风流浪 没有山...
    是为一阅读 215评论 0 0
  • 姑娘们约酒,没有心情不好,没有其他意思,就是想单纯聚聚,这有什么理由让人拒绝? 我们都是以前的同事,那时候的工作环...
    苏穆凉阅读 188评论 0 0