第九章 目标代码生成(1)
第9章 目标代码生成
寄存器的使用准则
寄存器先行准则:尽可能把变量的值驻留到寄存器
中,尽可能用寄存器中的现行值,以减少访问内存
的次数。例:
1. (:=, 10, X) 2. (×, X, X, t1) 3. (-, X, t1, t2) 4. (:=, t2, X)
执行 1 后,把10装入到某寄存器
RX中,而不送到X的内存单元, 这样,2、3、4就可以直接从RX 中取值,而不用访问内存单元了
end GetReg
例:中间代码(三地址码): (1) t1 := z * 6 (2) x := -4 (3) if x >= 76 goto (7) (4) x := x + 4 (5) y := t1 + x (6) goto (3)
指令种类
• 赋值 MOV • 比较 CMP • ≥转移 JLE • 转移 JMP • 累加 ADD • 相乘 MUL
寄存器的分配原则是选择代价最小的寄存器,这就涉 及到代价如何计算的问题。如果申请的是空闲寄存器,则 代价自然为0,否则属于剥夺性质。当一个寄存器R被剥夺 时,R的一些占有变量的现行值可能不在内存中,而且以后 可能引用,因此要产生一些回送现行值的Store指令(ST) 和重新装入寄存器的Load指令(LD)。则R的分配代价包括:
DL:取D(Dead)或 L(Live)。如果从K+1中间
代码开始,到A重新被赋值或基本块结束都没有引
用A,则A的DL值为D;否则为L。DL值在中间代码
步骤确定。例如:
1. (+,a,b,t1) 2. (-,t1,c,t2) 3. (×,t2,b,t3) 4. (:=,t3,x)
(+,D,L,L) (-ቤተ መጻሕፍቲ ባይዱD,D,L) (×,D,D,L) (:=,D,D)
代码生成
谢谢观看
③汇编语言代码,须经过汇编程序汇编后,变成为可执行的机器语言代码。
问题二
目标代码生成阶段应考虑直接影响到目标代码速度的三个问题:一是如何生成较短的目标代码;二是如何充 分利用计算机中的寄存器,减少目标代码访问存储单元的次数;三是如何充分利用计算机指令系统的特点,以提 高目标代码的质量。
程序编译
编译(compilation, compile) 1、利用编译程序从源语言编写的源程序产生目标程序的过程。 2、用编译 程序产生目标程序的动作。编译就是把高级语言变成计算机可以识别的2进制语言,计算机只认识1和0,编译程 序把人们熟悉的语言换成2进制的。编译程序把一个源程序翻译成目标程序的工作过程分为五个阶段:词法分析; 语法分析;语义检查和中间代码生成;代码优化;目标代码生成。主要是进行词法分析和语法分析,又称为源程 序分析,分析过程中发现有语法错误,给出提示信息。
框架生成
CodeSmith MyGenerator NHibernate. CodePlus CodeMaker EntitysCodeGenerate 等等
器
动软代码生成器 动软代码生成器是完全自主知识产权研发的为软件项目开发设计的自动代码生成器,也是一个软件项目智能 开发平台,其本身亦是由计算机语言开发的软件. Java代码生成器 这个工具能够读取数据库表结构,通过对字段类型、名称等分析得到需要的各种变量,根据模板生成相应的 pojo类、hibernate的xml配置文件、dao和service的接口和类。 Table:根据表结构建立的对象。 Column:根据表中每列建立的对象。 Generator:生成器核心类,主要负责根据表对象和读取FreeMarker模板生成最后的java代码文件。 GeneratorControl:控制生成过程等的一些参数,例如文件是否覆盖、文件编码等。 GeneratorProperties:读取配置文件的类,配置文件包括数据库连接信息和一些基本的参数配置。
编译原理第九章 运行时存储空间组织
– 堆区(new, malloc)
9.5 嵌套过程语言的栈式实现
• Pascal 的过程嵌套 嵌套层次:主程序0层 ······ 采用层数计数器,每逢Proc Begin加1,遇 Proc End则减1。
• 直接外层 • 编译器需要将过程的层数记录到符号表中
2)返回函数结果:累加器、寄存器
··· a:= 3 ··· P(a); Write(a); ···
传地址 8,8 8
举例
Procedure P(x) Begin
x:=x+5; writeln(x,a); End;
传结果 8,3 8
传值 8,3 3
举例
begin
Procedure P(x,y,z) …P(a+b,a,a)
初等类型数据采用确定“字长”,数组按列存放,边界对齐。
这样,可将过程活动单元(局部数据区)直接安排在 过程目标码之后,以便运行时访问。
9.3 Fortran静态存储分配(2)
数据区
返回地址 调用程序返回地址(调用恢复地址)
寄存器保护区 保存调用程序的寄存器运行环境
形式单元 形参
简单变量 数组 临时变量
P ->S ->Q =》R ->R
Program P; var a,x…
Top
R
procedure Q(b)
SP
var i…
R
procedure R(u,v)
动
var c,d…
态
begin… R… end {R} 链
Q
begin … R… end{Q} procedure S
第9章目标代码生成
下面我们学习一个例子,一加深其理解:
[例11。6] 考察下面中间代码序列 G1:
第十一章 目标代码生成
T1 := A+B T2 := A- B F := T1 * T2 T1 := A- B T2 := A – C T3 := B - C T1 := T1 * T2 G := T1 * T3 其对应的DAG如图 * F - n2 * T1
第9章目标代码生成(1)
源程序 中间 中间
编译前端
代码
代码优化
代码
代码生成器
目标程序
符号表
代码生成器的位置
第十一章 目标代码生成
代码生成器的输入包括中间代码和符号表中的信息。 目标代码一般有以下三种形式以定位 (代真) 。 (2)待装配的机器语言模块。当需要执行时,由连接 装入程序把它们和某些运行程序连接起来,转换成能 执行的机器语言代码。
第十一章 目标代码生成
7.1.1 数据类型
类型的合法性检查是判断数据类型是否与上下文的要求一致 数据类型是对该类型数据(变量或常量)的取值是否合法以 及对该类型数据的运算是否合法的一种说明。
第十一章 目标代码生成
7.1.2 数据结构
一个程序设计语言如允许使用的数组、记录、字符串、 表、栈等形式的数据结构,在编译程序中应为它们提供相 应的翻译。 为了能对数据结构中的元素进行引用,必须完成从逻辑结 构到能够访问这些数据元素的物理结构的映射。应考虑: 1映射算法相对简单,根据逻辑结构容易计算出物理地址 2从逻辑结构投影到物理结构时,不至于超界或存储溢出 3使用的数据结构承担这种程序设计语言的主要功能
int arr[10000]; void Winky() { register int *p; for (p = arr; p < arr + 10000; p++) *p = 1; }
河北科技大学 编译原理教学大纲
《编译原理》课程教学大纲一、教学内容和要求重点掌握:有限自动机、正规文法、正规表达式、LL(1)分析法、LR分析法、语法制导翻译等知识;掌握:递归下降分析法、优先分析法、属性文法、中间语言、运行时存储分配、代码优化、常用算法;理解:文法、语言及自动机间的关系、符号表的组织及作用、目标代码生成、查错与校错及面向对象的程序设计语言第一章绪论1.编译过程概述2.编译程序的逻辑结构3.编译程序的组织第二章前后文无关文法和语言(共7学时)1.语言、文法及其表示2.句型分析3.文法的化简与改造4.文法与语言的Chomsky分类第三章词法分析与词法分析程序1.设计词法分析程序应考虑的问题2.正规文法与状态转换图3.有限自动机4.正规表达式与正规集第四章语法分析与语法分析程序1.自顶向下的语法分析i)消除左递归ii)消除回溯的条件iii)递归下降分析iv)预测分析(LL(1)分析)2.自底向上的语法分析i)简单优先分析ii)算符优先分析iii)LR分析第五章语法制导翻译及中间代码生成1.属性文法及属性翻译文法的概念2.常见中间语言3.简单算术表达式及赋值语句的翻译4.布尔表达式的翻译5.控制语句的翻译6.含有数组元素的算术表达式及赋值语句的翻译7.过程说明及过程调用的翻译8.说明语句的翻译第六章符号表1.符号表的组织2.符号表的建立与查找第七章运行时的存储组织与分配第八章代码优化1.局部优化2.数据流分析原理3.循环优化第九章目标代码生成第十章查错与改错。
编译原理 目标代码生成 流程代码解析
N
将栈中元素存到数 组,出栈
目标代码生成流程图:
6
开始 扫描逆波兰式
结束
Y
逆波兰式扫
描完
N
滤掉逆波兰式中 逗号
当前字符串为是否 不为运算分量,逗号
Y
当前字符存入数 组
Y N
当前字符串为是 否不为运算分量
N
当前字符串是 否为运算分量
N是否为寄存器, 用@等字符代表寄存器
MOV
传送字或字节.
算术运算指令
ADD
加法
算术运算指令
SUB
减法
算术运算指令
MUL
无符号乘法
算术运算指令
DIV
无符号除法
……
……
……
5. 实验代码
/******************************************************************************/ #include<iostream> #include<string> #include<stack> using namespace std; string temp1(8,0),temp2(8,0),value1; /******************************************************************************/ bool Ispair(string expre) { bool flag=true; stack<char> s; for(int i=0;i<expre.length();i++) { if(expre[i]=='(') s.push(expre[i]); if(expre[i]==')') { if(s.empty()) { flag=false; return flag; }
编译原理第9篇
编译原理
第37页
编译原理
第38页
编译原理
二、嵌套层次显示表(display)和活动记录
为了提高访问非局部量的速度,还可以引用一个 指针数组,称为嵌套层次显示表。 每进入一个过程后,在建立它的活动记录区的同 时建立一张嵌套层次表display. 假定现进入的过程的层数为i,则它的display表 含有i+1个单元。 此表本身是一个小找,自顶向下每个单元依次存 放着现行层,直接外层,…,直至最外层(0层, 主程序层)等每一层过程的最新活动记录的基地 址。
第25页
编译原理 进入过程P后所做工作示意
P的数组区
第26页
返回地址
1 0
TOP
SP
P的活动记录 (长度为L)
调用过程
编译原理
(3)过程返回
C语言以及其它一些相似的语言含有return(E)的返 回语句,E为表达式。
假定E值已计算出来并已存放在某临时单元T中,可 将T只传送到某个特定寄存器(调用过程将从这个特 定的寄存器中获得P的结果)。
第14页
编译原理 简单的栈式存贮分配
适用于简单程序语言的实现:语言没有分程序结构, 过程定义不允许嵌套,但允许过程的递归调用,允许 过程含有可变数组。 C语言就是这样一种语言。其局部名称的存储分配, 可以直接采用栈式存储分配策略。
第15页
编译原理
1、栈式存储分配
使用栈式存储分配法意味着把存储组成一个栈。 运行时,每当进入一个过程(一个新的活动开 始)时,就把它的活动记录压入栈,从而形成 过程工作时的数据区,一个过程的活动记录的 体积在编译时是可静态确定的。 当该活动结束(过程退出)时,再把它的活动 记录弹出栈,这样,它在栈顶上的数据区也随 即不复存在。
程序设计语言 编译原理(第三版)第9章
TOP 32
d
31
c
30
v
29
u
28
2
27
11
SP 26 25
返回地址 17
24
d
23
c
22
v(形参)
21
u(形参)
20
2(形参个数)
19
11
18
返回地址
17
11
16
i
15
b(形参)
14 1(形参个数)
13
0
12
返回地址
11
5
10
i
9
c
8
0
7
0
6
返回地址
5
0
4
x
3
a
2
0
1
返回地址
0
0
25
9.5 嵌套过程语言的栈式实现
0
0
过程S中调 用Q时
过程P中 调用S时
23
过程Q中调用R时
TOP
24
d
23
c
22
v(形参)
21
u(形参)
20 2(形参个数)
19
11
18 返回地址
SP
17
11
16
i
15
b(形参)
14 1(形参个数)
13
0
12
返回地址
11
5
10
i
9
c
8
0
7
0
6
返回地址
5
0
4
x
3
a
2
0
1
返回地址
0
0
代码生成
如果y和/或z的当前值不再引用,在块的出口也不活 跃,并且还在寄存器中,则修改寄存器描述符
9.4 一个简单的代码生成器
9.4.3 寄存器选择函数
函数getreg返回保存x := y op z的x值的位置L
如果名字y在R中,且R不含其它名字的值,并且在执
9.4 一个简单的代码生成器
9.4.1 寄存器描述和地址描述 例:对a := b + c 如果寄存器Ri含b,Rj含c,且b此后不再活跃
产生ADD Rj, Ri,结果a在Ri中
如果Ri含b,但c在内存单元,b仍然不再活跃
产生ADD c, Ri,或者 MOV c, Rj
ADD Rj, Ri
9.2 目 标 机 器
9.2.1 目标机器的指令系统
选择可作为几种微机代表的寄存器机器
四字节组成一个字,有n个通用寄存器
R0,R1, …,Rn-1。
二地址指令: op 源,目的
MOV
{将源移到目的中}
ADD
{将源加到目的中}
SUB {在目的中减去源}
9.2 目 标 机 器
地址模式和它们的汇编语言形式及相关开销
9.3 基本块和流图
9.3.4 下次引用信息
为每个三地址语句x := y op z决定x、y和z
的下次引用信息
i: x :=… := x …
. . . 没有对x的赋值
k: … := … x
9.3 基本块和流图
对每个基本块从最后一个语句反向扫描到第 一个语句,可以得到下次引用信息
B2
prod := t6
t7 := i +1
i := t7
if i <= 20 goto B2
目标代码生成
编译程序最后一个阶段是目标代码生 成。它通常在语义分析后或者优化后的中 间代码上进行,并将中间代码转化为等价 的目标代码。
本章主要介绍
简单代码生成器的设计和构造方法
9.1 概述
我们知道,编译程序的最终目的是 将源程序翻译成等价的目标程序,为了 达到此目的,编译程序除了对源程序进 行词法分析、语法分析和语义分析外, 还必需将语义分析后或者优化后的中间 代码转换为等价的目标代码。
9.3
简单代码生成器
当翻译一个四元式如A=B op C时, 我们需要知道在基本块中还有哪些四元 式要对变量A、B、C进行引用。 2. 建立每个变量的待用信息和活跃信息 (1)待用信息
9.3
例
简单代码生成器
A在(i)定值 A在(j)引用
(i) A=T1 (j) T2 =A OP X
(j)为四元式(i)变量A的待用信息
9.3
简单代码生成器
(2) 活跃变量
基本块中所有的非临时变量均看 作基本块出口之后的活跃变量,临时 变量根据其在基本块出口之后是否被 引用来确定它是否为活跃变量。
9.3
简单代码生成器
(3) 计算变量待用信息和活跃信息 的算法:
输入:基本块及其入口语句号和 出口语句号 输出:附加待用信息和活跃信息 的基本块
依次把四元式变换成目标代码, 并在一个基本块内考虑如何充分利用 寄存器。 在设计代码生成器时,为考虑充分 利用寄存器,我们须要考虑下面两点:
9.3
简单代码生成器
1. 给出寄存器的分配原则
(1)把基本块内还要使用的变量的值 尽可能地保存在寄存器中; (2)在基本块内,把不再使用的变量 所占用的寄存器及时释放掉; (3)当到基本块出口时,将变量的值 存放在内存中。
深入学习:掌握编译原理与目标代码生成
深入学习:掌握编译原理与目标代码生成编译原理与目标代码生成是计算机科学与软件工程领域非常重要的一部分。
在这个领域中,我们研究的是将程序代码转换为可执行代码的过程,这涉及到了词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成等一系列的技术和算法。
编译器是实现编译过程的工具,它将高级语言源代码转换为机器可执行的目标代码。
而编译过程中的关键环节即为目标代码生成,它是将中间代码转换为特定硬件平台上可执行的机器代码的过程。
编译原理的研究从上世纪50年代开始,经历了多个阶段的发展。
早期的编译器通过逐行读取源代码,然后将其转换为汇编语言或机器码。
随着计算机硬件的发展和编程语言的演化,编译器的设计和实现也发生了重大变化。
编译原理的核心概念之一是语法分析,它将源代码转换为抽象语法树(AST)。
语法分析器通过识别词法单元(如关键字、标识符、操作符等)来构建AST,并使用上下文无关文法来定义语法结构。
常见的语法分析算法有递归下降分析和LR分析。
语义分析是编译过程中的另一个重要环节,它确保程序在语义上是正确的。
语义分析器会检查源代码中的类型错误、作用域错误、类型转换错误等,并为后续的代码生成阶段提供必要的信息和约束条件。
中间代码生成是编译器的一个重要步骤,它将AST转换为一种中间表示形式,例如三地址码或者虚拟机指令。
中间代码是对源代码的一种抽象表示,它简化了后续代码生成和优化的过程,并为优化算法提供了更好的机会。
代码优化是编译过程中的重要环节,它旨在提高目标代码的性能和效率。
代码优化器使用各种技术和算法,例如常量折叠、循环优化、指令调度等,来改进目标代码的执行效率。
最后一个环节是目标代码生成,它将中间代码转化为特定硬件平台的机器代码。
目标代码生成器需要考虑到硬件平台的特点和限制,并根据目标机器的指令集架构来生成高效的机器代码。
目标代码生成是编译器的最终阶段,它决定了程序的最终执行效果。
一个好的目标代码生成器应该可以充分利用硬件资源,减少指令的数目和执行时间,从而提升程序的性能。
目标代码生成
注意:表中信息是当前语句对前面语句的影响。
待用信息和活跃信息在四元式上的标记如下所示:
(1) T(3)L:=A(2)L-BFL
(2) U(3)L:=AFL-CFL
(3)V(4)L:=TFF+U(4)L
(4) DFL:=VFF+UFF
基本块内寄存器的分配
为随时掌握各寄存器的情况,设置了两个数组 ❖ 寄存器描述数组RVALUE
代码生成器的位置
源程序
中间
中间
编译前端 代码 代码优化 代码 代码生成器
目标程序
符号表
11.1 基本问题(1)
❖ 代码生成器的输入:
源程序的中间表示以及符号表中的信息。
❖ 目标代码的形式:
1.绝对机器代码 ➢ 能独立执行的机器语言代码,所有地址均已定位。 ➢ 优点:可立即执行。
2.可再定位机器代码 ➢ 待装配的机器语言模块。当需要执行时,由连接装入 程序把它们和某些运行程序连接起来,转换成能执行 的机器语言代码。 ➢ 优点:子程序可单独编译。
从后依次处理各个中间代码:对每个四元式
i: A:=B op C
1. 把符号表中变量A的待用信息和活跃信息附加到中间 代码i上;
2. 置符号表中A的待用信息和活跃信息分别为“非待用” “非活跃”;
3. 把符号表中B和C的待用信息和活跃信息附加到中间 代码i上;
4. 置符号表中B和C的待用信息均置为i,活跃信息均置 为“活跃”。
的待用信息链和活跃变量信息链。
计算待用信息的算法
1 符号表中增加“待用信息”栏和“活跃信息”栏
➢初值设置 对各基本块的符号表中的“待用信息”栏和“活 跃信息”栏置初值,即把“待用信息”栏置“非 待用”,对“活跃信息”栏按在基本块出口处是 否为活跃而置成“活跃”或“非活跃”。
目标代码生成
• (ENDPROC, —,—,—)或(ENDFUNC,—,—,—) 1. 将本层活动记录中保存的机器状态恢复过来,对 应一组读指令。 2. 删除本层活动记录,使动态外层的活动记录成为 当前活动记录; 3. 按1(top)中记载的返回地址返回。 目标代码为: ST top, sp LD sp , 0(top) // 作废当前活动记录 JMP 1(top) //按返回地址返回
(+,a,b,t1) LD R a; Add R b (*,X,t1,t2) ST R t1; Mult R X (*,t2,Y,t3) Mult R Y (*,t3,t1,t4) Mult R t1
输入/输出语句的翻译
• 输入语句:(READ, A )
IN R ST A , R
• 输出语句:(WRITE,A )
(5) 求值顺序的选择
改变求值顺序,使得保存中间运算结果的寄存器的个数较少。 例如,C语言表达式(j++)*(j++)*(j++)的求值 C语言函数调用中参数的计算顺序 将不讨论求值顺序问题。一般情况下,简单地就按源程序书 写顺序或内部中间表示生成的顺序生成目标代码。
(6) 代码生成程序的设计
最重要的是产生正确的代码,此外,易于实现、测试和维护 是代码生成程序的重要设计目标。
4. 填写动态链指针 ST 0(top), sp 5. 填写返回地址 LD R, A+5 // A ST 1(top), R // A+1 6. 生成过程活动记录 ST sp, top // A +2 ST top, top + sem[f].size // A+3 7. 生成转向过程f入口的指令 JMP sem[f].code // A+4 8. 如果是函数调用,则把函数值读到寄存器中 LD R , 4(top) // A+5 ST t , R
目标代码生成
R1 …
R7
15
寄存器分配算法
• 一般在生成四元式A:=B OP C的代码中,通常把左操作 数B取到寄存器 R 中,再和C 操作( C可在内存中,也可 在寄存中),R中的结果就是A的值,或者说A占用了R. • 寄存器的分配可以用RGEREG(QUAD,R)实现,QUAD是 待分配寄存器的四元式: i: A:=B OP C , (OP,B,C,A) ; R是分配的寄存器。 (算法需查看附在四元式i上的活跃与引用信息及上表中 的结构)
Jrop P’;
7
§9.3 代码生成程序的雏形
• 为每个基本块的生成高质量代码:
– 总的指令条数要少; – 尽可能利用寄存器,少产生访问内存的指令,为 此需要充分合理的利用寄存器:
• 尽可能把后面还要引用的变量仍保存在寄存器中; • 应把不再使用的变量所占用的寄存器及时释放掉; • 为此需引入两个概念:基本块内变量的引用信息和 活跃信息。
end end;
13
四元式的附加信息
序 四元式 号 1 (-, A, B, T) 2 (+ , A, B, A) 3 (-, A, C, U) 4 (+, C, D, V) 5 (+, T, B, V) 6 (+, V, U, W) 结果 (5,Y) (3,Y) (6,Y) 左变 量 (2,Y) 右变 量 (2,Y)
符号表的变化情况
A (N, Y)→(3, Y)→(N, N)→(2, Y)→(1,Y)
B (N, Y)→(5, Y)→(2, Y)→ (1, Y) T (N, N)→(5, Y)→(N, N) C (N, Y)→(4, Y)→(3, Y) U (N, Y)→(6, Y)→(N, N)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
SYMBL[X( L )]
…L
a
yyy
b
yy
c
yy
d
yy
t1 n y y n
t2 n y n
t3 n y n
t4
nyn
t5
nyn
x
yn
9.1.3 寄存器的分配问题
寄存器操作快且指令短,如何充分利用它?
⒈ 设置描述表: RDL(R0,R1,…, Rn):
用以记录寄存器的当前状态:如 RDL.R1=x 如何为
此外还有下述 操作码 op :
逻辑 运算
LT(<),GT(>),EQ(==),LE(<=),GE(>=),NE(!=) AND(&&),OR(||),NO(!)
※ 四元式目标代码翻译示例:
【例9.1】
⑴( + a b t1 ) ⑵( - t1 d t2 )
①LD R0,a ②ADD R0,b ③SUB R0,d
※ 附有活跃信息的四元式:
⑴(+ a(y) b(y) t1(y ) ) ⑵(* a(y) t1(y) t3(y) ) ⑶(/ t1(y) t3(n ) x(y) ) ⑷(= t1(n) _ i(y ) )
Ⅲ. 基本块内活跃信息求解算法
• 支持: ⑴ 在符号表上增设一个信息项( L )
name … L
⑴(+ a( y ) b( y ) t1( y )) ⑵(- c( y ) d( y ) t2( y )) ⑶(* t1( y )t2( n )t3( y ))
⑷(- a( y ) t3( n )t4( y )) ⑸(/ t1( n ) 2 t5( y )) ⑹(+ t4( n )t5( n ) x( y ))
活跃信息
⑵ 四元式中变量 X 的附加信息项:X( L )
算法
※ 取值: L=n/y(不活跃/活跃) ;
⑴ 初值:基本块内各变量 SYMBL[X( L )]分别填写:
若 X为非临时变量 则置 X( y ); 否则置 X( n )
⑵ 逆序扫描基本块内各四元式(设为 q:( B C A) ): 执行: ① QT[q:A( L )]:= SYMBL[A( L )];
含义: Ri:= (Ri)op(Rk)
操作码
变量的内 存地址
或 Ri:= (Ri)op(M)
【注】若 op 为单目运算,则
op Ri , Rk/M 含义是: Ri:= op(Rk/M)
※ 常用的指令:
取、存
LD Ri,Rk/M …… Ri:= (Rk/M) ST Ri,Rk/M …… Rk/M:=(Ri)
【例9.2】
⑴( + a b t1 ) ⑵( - c d t2 )
①LD R0,a ②ADD R0,b ③LD R1,c ④SUB R1,d
⑶( * t1 t2 t3 )
⑤MUL R0,R1
讨论 ⑴ 为了精简代码,四元式结果变量值并不急于存储!
⑵ 例9.1中的t1的值,系统如何知道是在寄存器R0中?
② SYMBL[A( L )]:=( n ); ③ QT[q:B,C( L )]:= SYMBL[B,C( L )]; ④ SYMBL[B,C( L )]:=( y );
※ 活跃信息生成过程示例:
【例9.4】基本块内下述四元 式序列如下:
QT[q: ] q:( B( L ) C( L ) A( L ))
转向
FJ Ri, M TJ Ri, M JMP _, M
…… 若 (Ri)==false 则转 M …… 若 (Ri)==true 则转 M …… 无条件转 M
算术 运算
ADD Ri,Rk/M SUB Ri,Rk/M MUL Ri,Rk/M DIV Ri,Rk/M
…… Ri:=(Ri)+(Rk/M) …… Ri:=(Ri)-(Rk/M) …… Ri:=(Ri)*(Rk/M) …… Ri:=(Ri)/(Rk/M)
即指明 当前变量 x 值在寄存器R1中!
A分配
⒉ 寄存器分配三原则:
寄存器?
设当前四元式: q: A = B C
⑴ 【主动释放】如果B已经在寄存器Ri中,则选择Ri:
① 若 B 活跃,则要保存B的值,方法是:若有空闲
寄存器Rj,则生成指令 ST Ri,Rj;
否则生成指令 ST Ri,B;
若可交
② 修改描述表:删除 B,填写 A。
⑶ 例9.2存在寄存器分配问题,显然,若t2仍然占用 寄存器R0,则t1值将丢掉!
9.1.2 变量的活跃信息
Ⅰ. 变量的定义点和应用点 B,C的应用点(q)
※ 设有四元式:q( B C A )
Ⅱ. 活跃变量与非活跃变量
A的定义点(q)
【活跃变量】一个变量从某时刻(q)起,到下一个定 义点止,其间若有应用点,则称该变量在q是活跃 的(y),否则称该变量在q是非活跃的(n)。
第 9 章 目标代码及其生成
目标生成是编译的最后一个阶段,其功能可表示如下:
中间代码 目标生成 目标代码
其中:
符号表
中间代码 ---- 逆波兰式,三元式,四元式,语义树;… 目标代码 ---- 机器语言,汇编语言,…
符 号 表 ---- 变量的语义词典,…
内容 提要
9.1 目标代码生成的基本问题 9.2 四元式的目标代码生成算法 9.3 一个简单代码生成器设计
【注】我们是在一个基本块内讨论变量的活跃信息的, 为了处理方便,假定:
⑴ 临时变量在基本块出口后是非活跃的(n);
⑵ 非临时变量在量的活跃信息:
x=(a+b)/(a*(a+b));i=a+b;
【解】令 A(l)中的 l 为变量A 在某点的活跃信息(y/n);
则有四元式序列:
⑴(+ a b t1 ) ⑵(+ a b t2 )
⑴(+ a b t1 ) ⑵(* a t1 t3 ) ⑶(/ t1 t3 x ) ⑷(= t1 _ i )
⑶(* a t2 t3 ) ⑷(/ t1 t3 t4 ) ⑸(= t4 _ x ) ⑹(+ a b t5 ) ⑺(= t5 _ i )
9.1 目标代码生成的基本问题
9.1.1 目标代码选择
大多数编译程序不产生绝对地址的机器代码,而是 以汇编语言程序作为输出使代码生成阶段变得容易。此 外,指令集的选择以及指令的执行速度问题都是重要因 素。为了使算法具有通用性,这里采用的是:
❖ 虚拟机及其指令系统: • 虚拟机寄存器 R0 , R1 ,… , Rn-1 • 虚拟机指令系统 ※ 指令的基本形式:op Ri , Rk/M