龙书是近期读过最硬核的一本专业书,让我回想起了本科学数字电路和电磁场经历的折磨。工作之后来认识这门学科,明显感觉心态不一样了。学生时代会去扣一些定义、公式的字眼,死磕练习题和课程项目。工作之后更多是抱着一种科普的心态,对于和工作内容业务无关的推理会选择性地跳过,更像在用大脑筛选出一个quick start文章一样,故而名为浅摘,摘录一些让我建立起对这门学科认知的知识和一些感觉比较有收获的点,不涉及算法推导细节。
第一章 引论
编译器就是将程序设计语言翻译成可被计算机或者虚拟机执行的形式的软件。解释器是另一种常见的语言处理器,不用生成目标程序,直接执行源程序操作。
预处理器负责将分割为多个模块的源程序聚合在一起,将宏转换为源语言。汇编器负责将编译器生成的汇编程序转换为机器码。链接器将相关的目标文件或者库文件链接。加载器将可执行文件加载到内存执行。
编译器可以分为分析和综合两部分。分析将源程序分解并计算语法结构,检查错误信息,生成符号表和中间形式;综合将中间形式构造成目标程序。两部分又被称为编译器的前后端
词法分析器将词素转换为词法单元<token-name, attribute-name>,其中attribute-name指向符号表条目
语法分析创建语法树,每一个内部节点表示一次运算。语义分析使用语法树和符号表来检查语义是否与语言定义一致,并进行类型检查和类型转换。中间代码生成器生成三地址代码,类似于汇编代码,每条指令右侧最多只有一个运算符。代码优化器对代码片段进行等效替换以简化和提速。代码生成器最后生成目标汇编代码
第二章 语法制导翻译器
上下文无关文法包括:
- 终结符号。词法单元,语言基本符号
- 非终结符号。语法变量,一串终结符号
- 产生式(production),stmt -> if (expr) stmt else stmt。头部为非终结符号,箭头读作可以表示为,体部由终结符号和非终结符号的序列组成
- 指定非终结符号为开始符号
从开始符号出发,不断将非终结符号(头)替换成产生式体最后得到的所有终结符号串的集合为文法定义的语言。
语法分析即接受一个终结符号串为输入,找出从文法开始符号推导到该串的方法,否则报告语法错误。因此语法分析树的根就是开始符号,叶子结点是终结符号或者空(∈),内部节点是非终结符号。语法树叶子结点从左到右即树的结果,也即推导的符号串
二义性:多棵分析树生成同一个给定的终结符号串
词法分析器:跳过空字符串换行符,识别关键字、运算符、普通数字或者字符串
静态检查包括语法检查(标识符不能重复、break出现的位置等)和类型检查
第三章 词法分析
词法分析器读入源程序字符序列,将其组装成词素,进而生成一个词法单元序列。两个处理阶段:1)扫描阶段,删除注释和压缩空白字符;2)词法分析阶段,生成词法单元
识别词素的关键在于模式匹配,正则表达是一种重要表示方式。词素长度不一,源程序往往需要处理大量的字符,通常会引入输入缓冲区并维护两个指针lexemeBegin and forward指针来截取候选词素
模式在实现层面即状态转换图
词素模式匹配冲突解决:1)选择最长的前缀;2)选择Lex程序中先被列出的模式
第四章 语法分析
第五章 语法指导翻译
第六章 中间代码生成
读完之后理解程度和第二章差不多
第七章 运行时刻环境
CSAPP OSTEP 重复内容
第八章 代码生成
编译器的最后一步,负责指令选择、寄存器分配和指派、指令排序。
目标程序是能够在目标机器上运行的程序,受目标机器的指令集体系结构影响,常见体系结构包括RISC、CISC、基于堆栈的结构。RISC寄存器多,指令集简单;CISC相反,寄存器较少,指令复杂;堆栈结构典型的就是JVM,JVM是字节码的解释器,提供了跨平台兼容性,选择下载JDK JRE时会选择不同OS和CPU的版本也就是这个原因。
寄存器分配:选择被存放在寄存器中的变量;寄存器指派:具体指定一个变量被存在哪个寄存器中
利用基本块和其流图来表示中间代码。基本块:块内不存在条件跳转的最大连续三地址指令序列;基本块的控制权切换形成了流图
局部优化:基本块本身实现优化;全局优化:检查信息在多个基本块之间的流动
局部优化通常先将基本块转换为DAG。优化方式包括:寻找局部公共子表达式,避免重复计算;消除死代码,避免执行计算值不会被使用的指令;相互独立语句的指令重排序,降低临时值在寄存器中保存的时间;依据代数规则重排运算顺序,简化计算过程;使用机器特有的高效指令可以降低运行时间,例如x++
第九章 机器无关优化
全局优化: 全局公共子表达式;复制传播;代码等效移动;数据流分析
第十章 指令级并行性
CPU都使用指令流水线。每条指令有多个执行的阶段(获取指令、解码、执行运算、访问内存、写回结果),每个时钟周期可以有不同的指令同时执行(位于不同的执行阶段即可)
代码调度是程序优化的形式之一,满足约束:1)控制依赖,所有执行的运算优化后也必须执行;2)数据依赖,运算结果优化前后必须相同;3)资源约束,不能超额使用机器资源
第十一章 并行性与局部性优化
仿射访问:几乎所有并行化和局部性优化的技术都假设对数组的访问是仿射的,即这些数组的下标表达式是循环下标的线性函数。
迭代空间:d层循环嵌套定义了一个d维的迭代空间,空间中的点是d元组,元组的值对应于各层循环的下标取值。在仿射访问情况下,迭代空间是一个多面体
第十二章 过程间分析
应用场景:虚方法调用;指针别名分析;并行化;
内联:只要不是递归的过程,我们可以把所有过程调用替换为过程代码本身的拷贝,并对结果应用过程内分析。从效果上看是过程间分析。过程拆分可以让程序易于维护,程序的组合内联可以加速代码的执行