第九章 目标代码生成(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:读取配置文件的类,配置文件包括数据库连接信息和一些基本的参数配置。
编译原理第九章 运行时存储空间组织

• 简单栈区(可以带递归,但不可以嵌套定义) • 复杂栈区(可以嵌套定义, pascal)
– 堆区(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
– 堆区(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篇

第36页
编译原理
第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、栈式存储分配
使用栈式存储分配法意味着把存储组成一个栈。 运行时,每当进入一个过程(一个新的活动开 始)时,就把它的活动记录压入栈,从而形成 过程工作时的数据区,一个过程的活动记录的 体积在编译时是可静态确定的。 当该活动结束(过程退出)时,再把它的活动 记录弹出栈,这样,它在栈顶上的数据区也随 即不复存在。
编译原理
第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
- 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