绪论
1.1 什么是编译?
编译:将高级语言(源语言)翻译成 汇编语言或者机器语言(目标语言)的过程。
-
可重定位 :在内存中存放的起始位置L不是固定的。
-
起始地址 + 相对地址 = 绝对地址
-
加载器:(1)修改可重定位地址;(2)将修改后的指令和数据放到内存中适当的位置。
-
链接器: (1)将多个可重定位的机器代码文件(包括库文件)连接到一起; (2)解决外部内存地址(是指一个文件中的代码可能会引用另一个文件中的数据对象或过程,那么这些数据对象或者过程的地址相对于这个文件来说就是外部地址)问题。
1.2 编译系统的结构
编译器的 阶段(phase):
编译器的能够将源程序映射为在语义上等价的目标程序,这个映射过程有两个部分组成:分析部分和综合部分。
-
分析部分(编译器的前端):
- 将源程序分解为多个组成要素,并在这些要素之上加上语法结构 ====》 使用这个结构来创建源程序的一个中间表示 ====》 检查源程序 ====没有按照正确的语法构成,或者语义上不一致====》 必须提供有用的信息,使得用户得以修改。
- 收集有关源程序的信息,并将信息存放在一个称为 符号表 的数据结构中。符号表将和中间表示一起传送给综合部分。
-
综合部分(编译器的后端):根据中间表示和符号表中的信息来构造用户期待的目标程序。
优化步骤的目的是在中间表示之上进行转换,以便后端程序能生成更好的目标程序。优化是可选的。
1.2.1 词法分析/扫描
主要任务:读入组成源程序的字符流(从左至右逐行扫描),识别并确定类型,并且将它们组织为有意义的词素(lexeme)的序列。对于每词素,词法分析器产生如下形式的词法单元(token)作为输出:
token:<token-name(种别码), attribute-value(属性值>
对于 一词一码 型,token的第二个分量均为空;
对于 多词一码 型,token的第二个分量为标识符名;
对于 一型一码 型,token的第二个分量为 字面量;
1.2.2 语法分析/解析
语法分析器使用由词法分析器产生成的各个词法单元的第一个分量来创建 树形 的中间表示。该中间表示给出了词法分析产生的词法单元的语法结构。常用的表示形式是语法树。树中内部节点表示一个运算,而该节点的子节点表示运算的分量。
1.2.3 语义分析
主要任务:
-
语义分析器使用语法树和符号表中的信息来检查源程序是否和语言定义的语义一致。
- 变量或过程未经声明就使用
- 变量或过程名重复是声明
- 运算分量类型不匹配
- 操作符和操作数之间的类型不匹配
- 数组下表不是整数
- 对 非数组变量 使用数组访问操作符
- 对 非过程名 使用过程调用操作符
- 过程调用的 参数类型或数目 不匹配
- 参数返回类型有误
-
收集标识符的属性信息,并将这些信息存放在语法树或符号表中,以便随后的中间代码生成过程使用。
- 种属:简单变量,复合变量(数组),过程
- 类型:整型,字符型
- 存储位置,长度
- 值
- 作用域
- 参数和返回信息
- 参数个数,参数类型,参数传递方式,返回值类型
符号表是用于存放标识符的属性信息的数据结构。
符号表使用字符串表的原因:为了节省内存和提高效率。字符串表将程序中的所有标识符(如变量名、函数名)统一存储为唯一的字符串,从而避免重复存储相同的字符串。符号表可以通过引用字符串表中的字符串来减少内存占用,同时简化符号表的管理和访问。在编译过程中,字符串表帮助编译器高效地处理和检索标识符,提高编译性能。
1.2.4 中间代码生成以及编译器后端
常见的中间表示形式:
* 三地址码
* 三地址码由类似于汇编语言的指令序列组成,每个指令最多有三个操作数。
常用三地址指令
地址可以具有如下形式之一:
* 源程序中的名字
* 常量
* 编译器生成的临时变量
三地址指令的表示:
* 四元式:(op, x, y, z)、
* 三元式
* 间接三元式
* 语法结构树/语法树
常用三地址指令
三地址的四元式例子:
目标代码生成:
- 目标语言生成以源程序的中间表示形式作为输入,并把它映射到目标语言
- 目标代码生成的一个重要任务是为程序中使用的变量合理分配寄存器
代码优化
- 为改进代码所进行等价程序变换,使其运行得更快一些,占用空间更少一些,或者二者兼顾。
- 分为机器无关代码优化和机器有关代码优化