第九章 目标代码生成
目标代码生成

(1 2 ) h a lt B 5
图7–1 程序流图
[解答] (1) 考虑变量a的情况:基本块B2中没有对a进行 定值,且引用的次数为1(e=a−b);基本块B3没有对a进 行定值,也没有引用a;基本块B4对a进行了定值,并 且定值前引用的次数为1(a=a−f)。根据执行代价节省数 的计算公式得到:
③ 删除RVALUE [Ri]中的M; (4) 给出R,返回。
例7.2 对例7.1,假设只有AX和BX是可用寄存器,用代码 生成算法生成目标代码和相应的RVALUE和AVALUE。
表7.2 例7.2的目标代码
四元式
目标代码
RVALUE
AVALUE
T=A−B U=A−C V=T+U
MOV AX, A SUB AX, B
和A是同一标识符,或者B在该四元式之后不再被引用,则选取Ri为所
需寄存器并转(4)。
不行则选用空闲寄存器
(2) 如有尚未分配的寄存器,则从中选取一个Ri为所需寄存器并转(4)。 再不行则选用待用位置最远的变量占用的寄存器
(3) 从已分配的寄存器中选取一个Ri为所需寄存器R。选取原则为:占用 Ri的变量的值也同时放在内存中,或者该值在基本块中要在最远的位置 才会引用到。这样,对寄存器Ri所含的变量和变量在内存中的情况必须 先做如下调整:
(1) 在原代码生成算法中,仅当变量在基本块中被定值时,其值才 存放在寄存器中。现在把寄存器固定分配给某变量使用,当该变量 在基本块中被定值前,每引用它一次就可以少访问一次内存,则执 行代价节省1。
(2) 在原代码生成算法中,如果某变量在基本块中被定值且在基本 块出口之后是活跃的,则出基本块时要把它在寄存器中的值存放到 内存单元中。现在把寄存器固定分配给某变量使用,出基本块时就 无需把它的值存放到其内存单元中,则执行代价节省2。
第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)
编译原理第九章 运行时存储空间组织

– 堆区(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
编译原理第三版课后习题答案

编译原理第三版课后习题答案编译原理是计算机科学中的一门重要课程,它研究的是如何将高级程序语言转换为机器语言的过程。
而《编译原理》第三版是目前被广泛采用的教材之一。
在学习过程中,课后习题是巩固知识、提高能力的重要环节。
本文将为读者提供《编译原理》第三版课后习题的答案,希望能够帮助读者更好地理解和掌握这门课程。
第一章:引论习题1.1:编译器和解释器有什么区别?答案:编译器将整个源程序转换为目标代码,然后一次性执行目标代码;而解释器则逐行解释源程序,并即时执行。
习题1.2:编译器的主要任务是什么?答案:编译器的主要任务是将高级程序语言转换为目标代码,包括词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成等过程。
第二章:词法分析习题2.1:什么是词法分析?答案:词法分析是将源程序中的字符序列划分为有意义的词素(token)序列的过程。
习题2.2:请给出识别下列词素的正则表达式:(1)整数:[0-9]+(2)浮点数:[0-9]+\.[0-9]+(3)标识符:[a-zA-Z_][a-zA-Z_0-9]*第三章:语法分析习题3.1:什么是语法分析?答案:语法分析是将词法分析得到的词素序列转换为语法树的过程。
习题3.2:请给出下列文法的FIRST集和FOLLOW集:S -> aAbA -> cA | ε答案:FIRST(S) = {a}FIRST(A) = {c, ε}FOLLOW(S) = {$}FOLLOW(A) = {b}第四章:语义分析习题4.1:什么是语义分析?答案:语义分析是对源程序进行静态和动态语义检查的过程。
习题4.2:请给出下列文法的语义动作:S -> if E then S1 else S2答案:1. 计算E的值2. 如果E的值为真,则执行S1;否则执行S2。
第五章:中间代码生成习题5.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.循环优化第九章目标代码生成第十章查错与改错。
第九章 目标代码生成(1)

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 ))
编译原理自测题附答案(有错)

第一章一.填空题1.编译程序的工作过程一般可以划分为词法分析、语法分析、语义分析与中间代码产生、优化和生成目标程序等几个基本阶段,同时还伴有符号表管理和出错处理。
2.若源程序是用高级语言编写的,目标程序是汇编或机器语言,则其翻译程序称为编译程序。
3.编译方式与解释方式的根本区别在于运行目标程序时的控制权在解释器而不是目标程序。
4.翻译程序是这样一种程序,它能将用甲种语言书写的程序转换成与其等价的乙种语言书写的程序。
5.对编译程序而言,输入数据是高级语言(源)程序,输出结果是低级语言(目标)程序。
6.运行编译程序的计算机称宿主机,运行编译程序所产生目标代码的计算机称目标机。
7.当把编译程序划分成编译前端和编译后端时,前端主要由与源语言有关但与目标机无关的部分组成,编译后端包括编译程序中与目标机有关的部分,编译后端不依赖于源语言而仅仅依赖于中间语言。
8.描述词法规则的有效工具是词法分析器,通常使用语法分析器来描述语法规则,使用语义分析(与中间代码产生)器描述语义规则。
二.综合题(该答案仅供参考)1、给出C语言编译程序对下面语句进行编译时从词法分析到目标代码生成5个分析阶段的分析过程。
c=a+b*30;(1)给出每个阶段的输入和输出代码或其它数据形式。
(2)给出符号表,说明在哪些阶段会对符号表进行填写或查找。
(3)编译过程是否进行了代码优化?若有,请指出优化之处,并给出属于哪种优化?答:词法分析:出入源程序;输出识别出的记号流。
c=a+b*30 id1=id2+id3*30语法分析器:输入记号流,构造句子结构;输出语法树。
=id1 +id2 *id3 30语义分析与中间代码生成:出入语法树,输出中间代码变量地址数值注:赋值阶段会对符号表进行填写或查找1. id1 0 c (itr,30,,t1)2. id2 4 x (*,id3,t1,t2)3. id3 8 y (+,id2,t2,t3)4. t1 12 30 (=,t3,,id1)优化:1.(*,id3,30.0,t1)2.(+,id2,t1,id1)精简掉多余的复写传播mulf #30.0,r2 mov id2,r1 sub r1,r2 mov r2,id1第二章一.填空题1.上下文无关文法包括以下四个组成部分:一组终结符号,一组非终结符号,一个开始符号,以及一组产生式。
编译原理第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、栈式存储分配
使用栈式存储分配法意味着把存储组成一个栈。 运行时,每当进入一个过程(一个新的活动开 始)时,就把它的活动记录压入栈,从而形成 过程工作时的数据区,一个过程的活动记录的 体积在编译时是可静态确定的。 当该活动结束(过程退出)时,再把它的活动 记录弹出栈,这样,它在栈顶上的数据区也随 即不复存在。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
9.3.2
代码生成算法
假设基本块中每个中间代码形式为 A=B op C。 对每条中间代码 i:A=B op C依次执行如下步骤:
(1)调用函数GETREG(i:A=B op C); (2)利用地址描述数组AVALUE[B]和AVALUE[C]确定变量B和C的现 行值存放位置B’和C’; (3)如果B’R,则生成目标代码: LD R,B’ op R,C’ ;否则 生成目标代码 op R,C’ ; 若B’或C’为R,则删除AVALUE[B]或 AVALUE[C]中的R; (4)令AVALUE[A]={R} ,并令RVALUE[R]={A}; (5)如果B和C的现行值在基本块中不再被引用,则释放所占用RK (即,删除RVALUE[RK]中的B或C以及AVALUE[B]中的RK )。
类型 直接地址型 寄存器型 变址型 间址型
2、其它指令的意义:
指令 意义 LD Ri,B (B)Ri ST Ri,B (Ri) B J X 无条件转移到X单元 CMP A,B 比较A、B单元的值,并根据A<B分别置CT为0 或1或2 J<X CT=0 则转移 J X CT=0 或 CT=1 则转移 J=X CT=1 则转移 J X CT 1 则转移 J>X CT=2 则转移 J X CT=2或 CT=1则转移
接上页:
按上述算法给出的结点次序, T[1],T[2],…,T[N],可以把DAG重新 生成一个等价的中间代码序列,根据 新序列中的中间代码次序,运用前面 的代码生成算法生成的目标代码较优。
9.4
代码生成器的自动生成技术
机器体系结构不统一 自动生成的代码的质量、生成速度 与机器无关的优化
各中间代码对应的目标代码:
序号 中间代码 1 A=B op C 2 A= op B 目标代码 序号 中间代码 LD Ri,B 6 goto X op Ri,C 7 if A rop B LD Ri,B goto X op Ri,Ri LD Ri,B 8 A=P LD Rj,I 9 P =A LD Ri,B(Rj) LD Ri,B LD Rj,I ST Ri,A(Rj) 目标代码 J X’ LD Ri,A CMP Ri,B J rop X’ LD Ri,*P LD Rj,A ST Ri,*P
第 9章
目标代码生成
以源程序的中间代码作为输入,产生等价 的目标代码作为输出,如图。
编译前端 中间 代码 代码优化 中间 代码生成器 代码 目标程序
源程序
符号表
代码生成器的输入包括中间代码和符号表中
的信息; 代码生成是把语义分析后或优化后的中间代 码变换成目标代码。
9.1
概述
1. 代码生成器的输入: 源程序的中间表示:如三地址代码; 符号表中的信息:在数据区中的相对地址 2. 目标程序: 绝对机器语言代码; 可再定位机器语言代码; 汇编语言程序。
计算变量待用信息的算法:
(1)开始时,符号表中各变量为“非待用”,变量在出口 之后是否活跃填入活跃信息栏; (2)从基本块出口到入口由后向前依次处理各个中间代 码。对每一条代码 i: A:=B op C,执行步骤:
把符号表中变量A的待用和活跃信息附加到中间代码i上; 把符号表中A的待用和活跃信息置为“非待用”、“非活 跃”; 把符号表中变量B.C的待用和活跃信息附加到中间代码i上; 把符号表中B.C的待用信息置为i,活跃信息置为“活跃”。
例 考察基本块 (1)T=A-B (2)U=A-C
(3)V=T+U
(4)W=V+U
寄存器描述和变量地址描述
1、在代码生成过程中,建立寄存器描述数组 RVALUE,动态地记录各reg是空闲的、已分 配给某个变量、或已分配给某几个变量。 2、在代码生成过程中,建立变量地址描述数 组AVALUE,动态地记录各变量现行值的存 放位置:是在某个reg中、在某主存单元中、 或在某reg和主存单元中。
优 化
为了能够进行上述优化,代码生成器必须了解 一些信息:
9.3.1
待用信息与活跃信息
变量在基本块内的待用信息:从基本块的 出口由后向前扫描,对每个变量建立相应 的待用信息链和活跃变量信息链。 简化:基本块中的所有临时变量均看作基本 块出口之后的非活跃变量;所有非临时变 量均看作基本块出口之后的活跃变量。
把目标机的每条指令的形式描述作为输入 将中间语言代码与这种描述进行匹配来产生相 应的指令。
问题:
采用由形式描述驱动的技术:
小结:
在基本块内,利用DAG重排中间代码生成目 标代码的次序,以得到较少的目标代码; 充分利用寄存器,使目标代码的执行更快; 选择合适的指令,使生成的指令序列更短 更高效。
接上页: 置初值; FOR K=1 TO N DO T[K]=null; i=N; WHILE 存在未列入T的内部结点 DO BEGIN 选一个未列入T但其全部父结点均已入T或无父结点的内部 结点n; T[i]=n; i=i-1; WHILE n的最左子结点m不为叶结且其全部父结均已入T中 DO BEGIN T[i]=m; i=i-1; n=m END END;
acde f:=a-d cdef
B2
acdf B3 e:=a-c
B4
bdef
分配好寄存器后,就可以生成目标代码。
与简单代码生成器不同之处在于: (1)固定分配了寄存器的变量用相应reg表示; (2)循环前置结点中存值到寄存器; (3)循环出口结点中存值到主存单元; (4)循环中每个基本块的出口,未固定分配 reg的变量按需要回存到主存单元,固定分 配了reg的变量不须回存到主存单元。
9.1
概述
代码生成着重考虑两个问题:( 优化 )
如何使生成的目标代码较短; 如何充分利用寄存器,减少目标代码访 问存储单元的次数。
9.2
假想的目标机器模型
采用一个模型作为目标机器:
1、具有多个寄存器,regs可作为累加器或变址 器;具有四种类型的指令形式
指令形式 op Ri,M 运算符op包括:ADD、SUB、 op Ri, Rj MUL、DIV等 op Ri,c(Rj) op Ri,*M op Ri, *Rj op Ri,*c(Rj)
9.3 简单代码生成器
功能:依次把每条中间代码变换成目标代码, 并且在一个基本块的范围内考虑充分利用 寄存器。
例:A=(B+C)*D+E
中间代码: T1=B+C T2= T1 *D A= T2 +E
(1) LD (2) ADD (3) ST (4) LD (5) MUL (6) ST (7)LD (8)ADD (9)ST
对基本块中的中间代码序列,按怎样的次 序来生成目标代码? 改写为如下中间代码 序列G’: T2=C+D T3=E-T2 T1=A+B T4=T1-T3
例 考查如下中间代码 序列G: T1=A+B T2=C+D T3=E-T2 T4=T1-T3
DAG的目标代码
G的目标代码:
1 2 3 4 5 6 7 8 9 10 LD ADD LD ADD ST LD SUB LD SUB ST R0,A R0,B R1,C R1,D R0,T1 R0,E R0,R1 R1,T1 R1,R0 R1,T4
G’的目标代码:
1 2 3 4 5 6 7 8 LD ADD LD SUB LD ADD SUB ST R0,C R0,D R1,E R1,R0 R0,A R0,B R0,R1 R0,T4
省去了 ST R0,T1 LD R1,T1
结论:Leabharlann 对表达式X=A*B+C*D的求值,从右往左算得到的 目标代码较优。 利用基本块的DAG,将基本块的中间代码序列 重新排列。 给DAG中的N个内部结点重新排序的算法:
LD
R0,d
B0
LD
a:=b+c acde f:=a-d cdef
B2
R1,b
bcdf B1 d:=d-b e:=a+f acdef b:=d+f
acdf B3 e:=a-c bdef ST R0,d R1,b
B6
cdef b:=d+c bcdef ST
ST
B4
R0,d
R1,b
B5
ST
5
DAG的目标代码
R,B R,C R, T1 R, T1 R,D R, T2 R, T2 R,E R,A
例:
(1) LD (2) ADD (3) ST (4) LD (5) MUL (6) ST (7)LD (8)ADD (9)ST R,B R,C R, T1 R, T1 R,D R, T2 R, T2 R,E R,A (1) LD (2) ADD (5) MUL (8)ADD (9)ST R,B R,C R,D R,E R,A
把变量a的值调入寄存器时,应替换哪个寄存 器的内容才能计算更快?
对于循环,把可用的几个寄存器固定分配给节省 执行代价最多的那几个变量。 节省代价计算公式: [USE(M,B)+ 2*LIVE(M,B)]
BL
其中,USE(M,B)=基本块B中对M定值前引用M的次数; LIVE(M,B)=1(当M在B中被定值且在B的出口之后是活跃的) 0(其他情况)
B) 2 * LIVE(M, B)] [USE(M,
BL
USE(M, B) 基本块B中对M定值前 引用M的次数 1 LIVE(M, B) 0 如果M在基本块B中 被定值且在B的出口之后 是活跃的 其它情况
a:=b+c
bcdf B1 d:=d-b e:=a+f acdef b:=d+f cdef b:=d+c bcdef