前言
参考课上PPT内容。 该学习笔记目前仅打算个人使用。
后续会进一步整理,包括添加笔记内容,标明参考资料。
更新中。。。
跳过目录
目录
- 一、可移植性和抽象机
-
- 可移植和可适应
- 信息流
- 基本模式
- 基本操作
- 抽象机
-
- 优点
- 抽象机代码
- 二、Pascal 的 P-code 抽象机
- P-code 指令
- 补充:中间代码生成实例——翻译成后缀式
一、可移植性和抽象机
可移植和可适应
如果花较小的代价就能将一个程序迁移到另一台机器上,则称该程序是可移植的(portable)
- 即迁移程序的开销要显著小于从头开始编制程序的开销。
如果一个程序能够容易地进行修改就能满足不同用户和系统的需求,那么称该程序是可适应的(adaptable)。
假定要将一个给定的编译程序从X机移植到Y机,则需要产生Y机的代码,为此必须重写现有编译程序的代码生成部分。
如果原来的编译程序已分成前端和后端两部分:
- 前端处理源语言
- 后端处理与目标机相关的部分
- 在前后端之间有一个良好的接口
那么移植的主要工作仅仅是改变与目标机相关的部分。
信息流
编译程序两部分之间的信息流:
- 从前端到后端:为源语言上的基本操作
- 从后端到前端:为目标机的信息。
两者的接口能够通过抽象机来实现,也即能够将源语言的各种基本操作(fundamentaloperation)映射到该抽象机的伪操作上。
基本模式
- 可以是源语言中的类型(如Pascal中的整型和字符型),也可以是用来构造源语言中更复杂类型(如Pascal中构造类型)的模式。
基本操作
是指用于描述一个语言编写的程序的最简单、最直接的操作。
抽象机
可为特定的源语言设计抽象机,例如有为Pascal语言设计的抽象机。
抽象机模型是根据源语言(如Pascal)中基本的操作和模式来建立的。
编译程序的前端将源程序翻译成抽象机代码,只需要将源语言的各种结构分解为抽象机的基本模式下的基本操作序列。
由基本模式和基本操作所组成的对偶构成了抽象机上的一条指令。
抽象机的体系结构形成一个环境,在该环境中模式和操作相互作用来模拟源语言。
抽象机的设计者一方面要使抽象机能很容易地模拟源语言的各种结构所规定的操作,同时,还必须考虑能在实际计算机上高效地实现该抽象机的操作。
优点
-
使编译程序的前端和后端清楚地分离。
将一个编译程序移植到一台新的机器上,仅需要开发一个编译程序的新的后端,对前端也只需做少量的修改(必要时)。
例:在 n 台不同的机器上实现 m 种不同语言的编译
- 若不使用某种中间代码形式(如抽象机代码),那么必须要编写 m × n 个不同的编译程序。
- 若使用抽象机代码,则仅需要m个前端和n个后端。使用这种方法,m × n 个不同的编译程序能够由 m + n 个部分组合而成。
如今,对一种程序设计语言,为几种不同的目标机产生高效的目标代码已是一件十分容易的事了。
一个较为困难的问题是要有一台抽象机,由该抽象机要能为几种程序设计语言产生高效的目标代码,同时,它又要能够高效地模拟所有的程序设计语言。
因为各种程序设计语言(如FORTRAN、Lisp、Basic、Pascal和C)是有很大不同的,要找到这样的抽象机几乎不可能。
在计算机中,伪机器码可被微程序设计为指令组由硬件执行。
抽象机代码
许多Pascal编译系统生成的中间代码是一种称为P-code的抽象代码。
- P-code的“P”即“Pseudo”。
目前广泛使用的Java语言,其编译器生成的代码实际上也是一种抽象机(即Java虚拟机)的代码,称为字节码(byte code)。
二、Pascal 的 P-code 抽象机
既然是“抽象机”,就是表示它并不是实际的物理目标机器而通常是虚拟的一台“堆栈计算机”。
该堆栈式计算机主要由若干寄存器、一个保存程序指令的储存器(PC)和一个堆栈式数据及操作存储组成。
栈:
- 用于存放所有可按源程序中的数据声明进行直接寻址的数据
堆:
- 用于存放由程序员直接控制,即通过new语句所创建出来的数据
寄存器:
- PC:程序计数器,指向当前执行的指令。
- NP:堆指针,指向当前空闲堆空间的起始位置。
- SP:指针,指向当前栈顶位置。
- BP:基地址指针,即指向当前活动记录的起始位置。
- MP:标志指针,指向当前活动记录的基地址。
- EP:顶指针,指向当前程序模块在栈中的最高位置。
栈式计算机的存储情况如下:

问题:全局变量放在哪里?
运行P-code的抽象机没有专门的运算器或累加器,所有的运算(操作)都在运行栈的栈顶进行,如要进行运算
d := ( a + b ) * c
生成P– code序列为:
取a LOD a
取b LOD b
+ ADD
取c LOD c
* MUL
送d STO d
P-code 指令
P-code实际上是波兰表示形式的中间代码。
编译程序生成P-code指令程序后,我们可以用一个解释执行程序( interpreter)来解释执行P-code ,当然也可以把P-code再变成某一机器的目标代码。
显然,生成抽象机P-code的编译程序是很容易移植的。
补充:中间代码生成实例——翻译成后缀式
概念:拉链回填
begin
k := 100;
L: if k > i + j then
begin
k := k – 1;
goto L;
end
else k := i ^ 2 - j ^ 2;
i:= 0;
end
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | … |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
- k := 100;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 k 100 := - k > i + j
1 2 3 4
L标号
5 6 7 8 9 10 11 12 13 14 15 16 k 100 := k i j + > - if then
1 2 3 4
L标号
5 6 7 8 9
label1
10 11 12 13 14 15 16 k 100 := k i j + > ? jez - 无法确定的地址先填入0。
- 一旦地址确定“回填”之!
1 2 3 4
L标号
5 6 7 8 9
label1
10 11 12 13 14 15 16 k 100 := k i j + > jez - k := k – 1;
1 2 3 4
L标号
5 6 7 8 9
label1
10 11 12 13 14 15 16 k 100 := k i j + > jez k k 1 - := - goto L;
1 2 3 4
L标号
5 6 7 8 9
label1
10 11 12 13 14 15 16 k 100 := k i j + > jez k k 1 - := 4 17 j - else
1 2 3 4
L标号
5 6 7 8 9
label1
10 11 12 13 14 15 16 k 100 := k i j + > jez k k 1 - := 4 17 18
label2
19 20 21 22 23 24 25 26 27 28 29 30 31 32 j ? j - k := i ^ 2 - j ^ 2;
1 2 3 4
L标号
5 6 7 8 9 10 11 12 13 14 15 16 k 100 := k i j + > 20 jez k k 1 - := 4 17 18
label2
19 20
label1
21 22 23 24 25 26 27 28 29 30 31 32 j j k i 2 ^ j 2 ^ - := - i := 0;
1 2 3 4
L标号
5 6 7 8 9
label1
10 11 12 13 14 15 16 k 100 := k i j + > 20 jez k k 1 - := 4 17 18
label2
19 20
label1
21 22 23 24 25 26 27 28 29
label2
30 31 32 j 29 j k i 2 ^ j 2 ^ - := i := …
注:
- 该中间代码程序有死代码,即永远执行不到的代码(18、19)