绪论
编译器在语言处理系统中的位置
-
预处理器 (Preprocessor)
- 把存储在不同文件中的源程序聚合在一起
- 把被称为宏的缩写语句转换为原始语句
-
可重定位(Relocatable)
- 在内存中存放的起始位置L不是固定的
-
链接器 (Linker) /加载器 (Loader)
库文件及其它可重定位目标程序
- 加载器
- 修改可重定位地址;
- 将修改后的指令和数据放到内存中适当的位置
- 链接器
- 将多个可重定位的机器代码文件(包括库文件)连接到一起解决外部内存地址问题
- 起始位置 +相对地址=绝对地址
- 加载器
编译器的结构
词法分析/扫描(Scanning)
词法分析的主要任务
从左向右逐行扫描源程序的字符,识别出各个单词,确定单词的类型。
将识别出的单词转换成统一的机内表示——词法单元(token)形式
token:< 种别码,属性值 >
- 一词一码不必多说;
- 一型一码:如1、2、3、4 种别码都是整形,但属性值不同(1,2,3,4)。
- 多词一码:如a、i、num 种别码都是变量,但属性值不同(a,i,num)。
例:词法分析后得到的token序列
语法分析概述
语法分析 ( parsing)
语法分析器(parser)从词法分析器输出的token序列中识别出各类短语,并构造语法分析树(parse tree)
语法分析树描述了句子的语法结构
例1:赋值语句的分析树
例2:变量声明语句的分析树
文法:
< D > → < T > < IDS >;
< T > → int | real | char | bool
< IDS > → id | < IDS >, id
输入:
int a , b , c ;
语义分析概述
语义分析的主要任务
收集标识符的属性信息
- 种属 (Kind)
简单变量、复合变量(数组、记录、…)、过程、… - 类型 (Type)
整型、实型、字符型、布尔型、指针型、… - 存储位置、长度
- 值
- 作用域
- 参数和返回值信息
参数个数、参数类型、参数传递方式、返回值类型、…
参数和返回值信息
例如:
begin
real x[8];//一个整形8位 0x0-0x64
integer i, j; //如上
……
end
语义分析收集到的所有信息最后会放在符号表中。
符号表中为什么要设计字符串表这样一种数据结构?
不同标识符的NAME字段长度往往不同,如果以变长存储,就会使得符号表的查询效率降低,增大符号表管理的难度;
而若是以定长存储,很显然必须为该字段按照最大长度分配空间,而造成空间的严重浪费。
因此,只在NAME字段里面存储类似指针功能的定长字段,既能节省空间,又不会显著影响查询效率,是一种较好的设计思路。
作者:Yuan
链接:https://www.zhihu.com/question/267753934/answer/2178081626
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
语义检查
- 变量或过程未经声明就使用
- 变量或过程名重复声明
- 运算分量类型不匹配
如整形与浮点数相加 - 操作符与操作数之间的类型不匹配
- 数组下标不是整数
- 对非数组变量使用数组访问操作符
- 对非过程名使用过程调用操作符
- 过程调用的参数类型或数目不匹配
- 函数返回类型有误
中间代码生成及编译器后端概述
常用的中间表示形式
三地址码 (Three-address Code)
三地址码由类似于汇编语言的指令序列组成,
每个指令最多有三个操作数(operand)
常用的三地址指令
ps:红色部分表达指令操作符
地址可以具有如下形式之一
- 源程序中的名字 (name)
- 常量 (constant)
- 编译器生成的临时变量(temporary)
三地址指令在数据结构中的表示
- 四元式 (Quadruples)
- (op, y, z, x) //ps:(操作符,源操作数,源操作数,目标操作数)
- 三元式 (Triples)
- 间接三元式 (Indirect triples)
三地址指令的四元式表示
语法结构树/语法树 (Syntax Trees)
中间代码生成的例子
ps:j表示jump,为跳转指令。 j<表示a<b就跳转。j表示无条件跳转。
while a<b do
if c<5 then
while x>y do
z=x+1;
else x=y;
c代码对应指令四元式⇅
100: ( j<, a ,b , 102 )//如果a<b,刚跳转过到102
101: ( j , - , - , 112 )//无条件跳转到112,即跳出循环
102: ( j<, c , 5 , 104 )
103: ( j , - , - , 110 )
104: ( j>, x , y , 106 )
105: ( j , - , - , 100 )
106: ( + , x , 1 , t1 ) //赋值t1=x+1
107: ( = , t1 , - , z )//赋值z=t1
108: ( j , - , - , 104 )
109: ( j , - , - , 100 )
110: ( = , y , - , x )
111: ( j , - , - , 100 )
112:
目标代码生成
目标代码生成以源程序的中间表示形式作为输入,并把它映射到目标语言
目标代码生成的一个重要任务是为程序中使用的变量合理分配寄存器
代码优化
为改进代码所进行的等价程序变换,使其运行得更快一些、占用空间更少一些,或者二者兼顾