目标代码的生成第8章
LGY_BY08语法制导翻译和中间代码生成

表达式文法 E—>T+T| T or T T—>n | b 1 2 ET + T 1 {T .type = int 2 1 T .type= T .type E.type :=int} 1 2 E T or T 1 {T .type = bool 2 1 T .type= T .type E.type :=bool} T n { T.type := int} T b { T.type := bool}
赖国勇 20
2013-7-27
8.2
属性赋值
属性有不同的类型,可以象变量一样地被赋值。
赋值规则附加于语法规则之上。 赋值与语法分析同时进行,赋值过程就是语义处理过 程。 在推导语法树的时候,诸属性的值被计算并通过赋值 规则层层传递。 有的从语法规则左边向右边传,有的从右边向左边传。 语法推导树最后完成时,就得到开始符号的属性值, 也就是整个程序的语义。
8.1
确定标识符类型的语义描述
(1)P→D;E (2)D→D;D (3)D→id:T {addtype (id. Entry, T. type)} (4)T→char {T. Type:= char} (5)T→integer {T. Type:= integer} (6)T→↑T1 {T. Type:= pointer (T1. type)} (7)T→array [num]of T1
2013-7-27
赖国勇
9
8.1
解释执行动态语义
(计算)生成代码(中间代码或目标代码)
2013-7-27
赖国勇
10
8.1
如 if B then S1 else S2
第十一章目标代码生成

这两个问题直接影响代码的执行速度。
基本问题
➢代码生成器的输入 ➢目标程序 ➢选择适当的代码指令 ➢寄存器分配方案 ➢计算顺序
例:考察下面中间代码序列 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
T1 := E/F T2 := D + T1 T3 := G + T2 T4 := C * T3 T5 := H * T4 T6 := B + T5 T7 := A + T6 T8 := I * J T9 := T7 + T8
最优的目标代码:
LD R0, E
DIV R0, F
ADD R0, G
MUL R0, H
T3 := B-C ; T2 := A- C ; S1 := A – B ; T1 := S1 *
T2;
G := T1 * T3; S2 := A + B ; F := S2 * S1 。如前所 述按后一个中间代较好理解,这里不再重述课 本内容。
T4 := E-F LD R0, E R0 中
SUB R0, F
T5:=T3* T4 MUL R1, R0 R1中
储器中
LD R0, T2
W:=T2/T5 DIV R0, R1 R0中
储器中
ST R0, W
R0含 T4
T4 在
R1含 T5
T5 在
R0含T2 T2在R0和存
R0含W
W在
W在R0和存
第十二章并行编译基础
目标代码一般有以下三种形式:
第8章符号表与错误处理

第8章 符号表与错误处理
8.2.1 语法错误的校正 对源程序错误的处理通常有两类不同方法:一类是
对错误进行适当的修补,以便编译工作能够继续下去; 另一类是跳过有错误的那个语法成分,以便把错误限制 在一个尽可能小的局部范围内,从而减少因某一错误而 引起的一连串假错。第二类方法又称为错误的局部化法。
1.单词错误的校正 词法分析的主要任务是把字符串形式的源程序转换 为一个单词系列。由于每一类单词都可以用某一正规式 表示,因而在识别源程序中的单词符号时,通常采用一 种匹配最长子串的策略。如果在识别单词的过程中发现 当前余留的输入字符串的任何前缀都不能和所有词型相 匹配,
第8章 符号表与错误处理
首先讨论自上而下分析中的错误校正问题。在语
法分析过程的每一时刻,总可以把源程序输入符号串
a1 a2…an划分为如下形式:
a1a2 …an= w1aiw2
(8.1)
其中w1是已经扫描和加工过的部分,ai为现行输入 符号,而w2则是输入串的余留部分。假定编译程序现 在发现了源程序中的一个语法错误,这对自上而下分
在多数编译程序中不是采用“最小海明距离法”来校 正错误,而是采用一种更为简单、有效的方法。这种方法 的依据是程序中所有错误多半属于下面几种情况之一:
第8章 符号表与错误处理
(1) 拼错了一个字符; (2) 遗漏了一个字符; (3) 多写了一个字符; (4) 相邻两字符颠倒了顺序。 通过检测这四种情况,编译程序可查出源程序中 大部分的拼写错误。对这些错误进行简单的检测与校 正的方法为: (1) 从符号表中选出一个子集,使此子集包含所有 那些可能被拼错的符号; (2) 检查此子集中的各个符号,看是否可按上述四 种情况之一把它变为某一正确的符号,然后用它去替 换源程序中的错误符号。
编译原理第三版课后习题答案

编译原理第三版课后习题答案编译原理是计算机科学中的一门重要课程,它研究的是如何将高级程序语言转换为机器语言的过程。
而《编译原理》第三版是目前被广泛采用的教材之一。
在学习过程中,课后习题是巩固知识、提高能力的重要环节。
本文将为读者提供《编译原理》第三版课后习题的答案,希望能够帮助读者更好地理解和掌握这门课程。
第一章:引论习题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:什么是中间代码?答案:中间代码是一种介于源代码和目标代码之间的表示形式,它将源代码转换为一种更容易进行优化和转换的形式。
目标代码生成

四元式
目标代码 RVALUE AVALUE
T=A−B U=A−C V=T+U D=V+U
MOV AX, A SUB AX, B MOV BX, A SUB BX, C
ADD AX, BX
ADD AX, BX
AX含有T
AX含有T BX含有U AX含有V BX含有U
AX含有D
T在AX中
T在AX中 U在BX中 V在AX中 U在BX中
处理完基本块中所有的四元式后,对现行只在 某寄存器中的每个变量,如果它在基本块出口之后 是活跃的,则要用MOV指令把它在寄存器中的值存 放到数据区以它命名的内存单元中。
第7章 目标代码生成
为进行这一工作,我们利用寄存器描述数组 RVALUE来决定其中哪些变量的现行值在寄存器中, 再利用地址描述数组AVALUE来决定其中哪些变量 的现行值尚不在其内存单元中,最后利用活跃变量 信息来决定其中哪些变量是活跃的。例如,由例7.2 的表7.2查RVALUE栏可知:U和D的值在寄存器中, 而从AVALUE栏知U和D的值都不在内存单元中, 又由例7.1表7.1知,D在基本块出口之后是活跃变量, 所以,在表7.2所生成的目标代码后面还要生成一条 目标代码:
第7章 目标代码生成
例如,一C语言语句为A=(B+C)*D+E,把它翻 译为四元式G:
T1=B+C T2=T1*D A=T2+E 如果不考虑代码的效率,可以简单地把每条中 间代码(四元式)映射成若干条目标指令,如将x=y+z 映射为:
MOV AX, y /*AX为寄存器*/
ADD AX, z
MOV x, AX 其中,x、y、z均为数据区的内存变量。
例7.1 考察基本块: (1) T=A−B (2) U=A−C (3) V=T+U (4) D=V+U
第08章语法制导翻译和中间代码生成

常用的中间语言
• 三地址代码(四元式) 三地址代码(四元式) • 语法结构树(三元式) 语法结构树(三元式) • 后缀式
特点
• 形式简单、语义明确、便于翻译 形式简单、语义明确、 • 独立于目标语言
29
三元式和树形表示
• 每个三元式由三个部分组成,分别是:算符op, 每个三元式由三个部分组成,分别是:算符op, op 第一运算对象ARG1和第二运算对象ARG2 ARG1和第二运算对象ARG2。 第一运算对象ARG1和第二运算对象ARG2。运算 对象可能是源程序中的变量, 对象可能是源程序中的变量,也可能是某个三 元式的结果,用三元式的编号表示。 元式的结果,用三元式的编号表示。 • 表达式的树形表示很容易实现:简单变量或常 表达式的树形表示很容易实现: 数的树就是该变量或常数自身,如果表达式e1 数的树就是该变量或常数自身,如果表达式e1 e2的树分别为T1和T2,那么e1+e2 e1*e2, 的树分别为T1 e1+e2, 和e2的树分别为T1和T2,那么e1+e2,e1*e2, e1的树分别为 的树分别为: -e1的树分别为:
9
–lexval 是单词 digit 的属性 lexval
例8-3 3*5+4 的
语法树与属性计算
L Print(19) E.val=19
E.val=15 T.val=15 T.val=3 F.val=3 digit.lexval=3 *
+
T.val=4 F.ቤተ መጻሕፍቲ ባይዱal=4
F.val=5 dgit.lexval=5
• 用中间语言过渡的好处: 用中间语言过渡的好处:
–便于编译系统的实现、移植、代码优化 便于编译系统的实现、移植、 便于编译系统的实现
编译第8章

• 属性变量=属性表达式
1、属性文法定义
属性文法(attribute grammar)是一个三元 组:A=(G,V,F),其中 G:是一个上下文无关文法 V:有穷的属性集,每个属性与文法的一个终结符或非 终结符相连,这些属性代表与文法符号相关信息, 如它的类型、值、代码序列、符号表内容等等 .属 性与变量一样,可以进行计算和传递。属性加工 的过程即是语义处理的过程。 F:关于属性的属性断言或一组属性的计算规则(称为 语义规则) . 断言或语义规则与一个产生式相联,只 引用该产生式左端或右端的终结符或非终结符相 联的属性.
addtype
id3
addtype
例5-4:real id1,id2,id3 的分析树和属性计算
8.3 中间代码的形式
何谓中间代码: 源程序的一种内部表示,不依赖目标机的 结构,易于机械生成目标代码的中间表示。 为什麽要此阶段 逻辑结构清楚; 利于不同目标机上实现同一种语言; 利于进行与机器无关的优化; 这些内部形式也能用于解释;
• 语义处理
–例:变量的存储分配 –例:表达式的求值 –例:语句的翻译(中间代码的生成)
• 总目标:生成等价的中间代码
语义处理方法
• 对应每一个产生式编制一个语义子程序, 当一个产生式获得匹配时,调用相应的 语义子程序实现语义检查与翻译。 • 在产生式的右部的适当位置,插入相应 的语义动作,按照分析的进程,执行遇 到的语义动作。
8)若把语义子程序改成产生某种中间代 码的动作,就能在语法分析制导下,随 着分析的进展逐步生成中间代码。 9)若把语义子程序改成产生某种机器的 汇编语言指令,就能随着分析的进展逐 步生成某机器的汇编语言代码。
《编译原理》教学大纲

《编译原理》教学大纲一、课程概述编译原理是计算机科学与技术专业的一门重要课程,也是软件工程领域的基础课程之一、本课程通过对编译器的原理和实现技术的学习,使学生掌握编译器的设计和实现方法,培养学生独立解决实际问题的能力。
二、教学目标1.理解编译器的基本原理和工作流程;2.掌握常见编译器的构建方法和技术;3.能够设计和实现简单的编译器;4.培养分析和解决实际问题的能力。
三、教学内容和教学进度1.第一章:引论1.1编译器的定义和分类1.2编译器的基本工作流程2.第二章:词法分析2.1编译器的基本结构2.2词法单元的定义和识别方法2.3正则表达式和有限自动机3.第三章:语法分析3.1语法分析的基本概念3.2语法规则的定义和表示方法3.3自顶向下的语法分析方法3.4自底向上的语法分析方法4.第四章:语义分析4.1语义分析的基本概念4.2属性文法和语法制导翻译4.3语义动作和符号表管理5.第五章:中间代码生成5.1中间代码的定义和表示方法5.2基本块和控制流图5.3三地址码的生成方法6.第六章:优化6.1优化的基本概念和原则6.2常见的优化技术和方法6.3编译器的优化策略7.第七章:目标代码生成7.1目标代码生成的基本原理7.2目标代码的表示方法和存储管理7.3基本块的划分和目标代码生成算法8.第八章:附加主题8.1解释器和编译器的比较8.2面向对象语言的编译8.3并行编译和动态编译四、教学方法1.理论教学与实践相结合,注重教学案例的分析和实践;2.引导学生主动探索,注重培养学生的自主学习能力;3.激发学生的兴趣,鼓励学生提问和讨论。
五、考核方式1.平时成绩:包括课堂测验、作业和实验报告等;2.期末考试:闭卷笔试,主要考查学生对编译原理的理论知识和实践能力的掌握程度。
六、参考教材1.《编译原理与技术》(第2版),龙书,机械工业出版社,2024年2.《现代编译原理-C语言描述》(第2版),谢路云,电子工业出版社,2024年七、参考资源1. 实验环境:Dev-C++、gcc、llvm等2.相关网站:编译原理教学网站、编译器开源项目等八、教学团队本课程由计算机科学与技术学院的相关教师负责教学,具体安排详见教务处发布的教学计划。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
8.1.2 目标代码
本章采用汇编语言代码作为目标代码,但不针对某种特 定的目标机指令系统或汇编语言来生成目标代码,而是假设 有一台计算机,其指令系统等均按某种需要而设定,为教学 目的往往采取这种虚拟机目标代码形式。
2.按真转编写程序
8.2.2 虚拟机的汇编指令
3.按假转编写程序
8.3 从中间代码生成目标代码
8.3.1 从逆波兰表示生成目标代码 8.3.2 从四元式序列生成目标代码
8.3.1 从逆波兰表生成目标代码
从表达式的逆波兰表示生成相应的目标代码的算法可给出如下。 首先设立一个运算分量栈(栈),用来存放暂时不能处理的运算 分量的名(即工作单元地址)以及中间运算结果的名(即存放中间结 果的工作单元地址或累加器AC)。 其具体算法步骤如下。 从左到右扫描所给定的逆波兰表达式中的每个符号: 若扫描到运算分量,则将其下推入运算分量栈。 若扫描到运算符,按该运算符是几目运算,把运算分量栈 中相应个数的栈顶元素取出,生成该运算相应的目标代码,此后 栈上退去相应个数的运算分量,运算结果存放在“AC”中,把“AC” 标志入运算分量栈。 如此继续,直到整个逆波兰表达式处理完毕为止。
该虚拟机是一台地址单累加器的计算机,用“AC”表示该 累加器;设OP为操作码,d为地址,则指令: 0P d
表示 AC OP d=>AC
即累加器AC中的内容与d中的内容进行某种运算,结果送 到累加器AC中;其内存容量足够大;提供了21条符号汇 编指令。
8.2.2 虚拟机的汇编指令
虚拟机的汇编指令如表8.1所示。
各种代码的形式
• 中间代码: 后缀式,三地址代码,四元式 • 符号表中的项:名字,类型,嵌套深度,偏移
量 • 目标代码:绝对机器代码,可重定位代码,汇
编
代码生成器的输出必须是正确和高质量的 产生最优化代码的问题是不可判定的
目标代码的生成
目标代码生成是编译程序的最后一个工作阶段。它取先行阶段所产 生的源程序的中间语言或优化后的中间语言表示作为输入,产生等价的 目标代码作为输出,如下图8.1所示。
如果对X:=a[10]语句不采用填地址指令,其程序为 100 CLA <10> 101 ADD <<a[0]>> 102 STO M 103 ICA M 104 STO X
其中M为临时工作单元变量,用来存放<a[10]>,第103条指令使用间接取的方式,实现了X: = a[10]。
8.2.2 虚拟机的汇编指令
目标代码生成程序的输入、输出
编译程序的最终输出是目标代码,这在编译程序的代码 生成阶段完成,也可在语义分析阶段生成。一般地,代码生 成部分称为代码生成程序。代码生成程序的功能是为源程序 生成与之等价的目标机器代码。也就是说,其输入是由先行 端产生的源程序的中间表示,输出是目标代码。
假定在代码生成前,编译的先行端已扫描、分析和翻译 源程序,成为足够详细的中间表示,这样,中间语言中名字 的值可以表示为目标机器能够直接操作的量(位、整数、实 数、指针等),还假定必要的类型检查与插入类型转换等已 完成,不仅语法上正确,语义也是正确的,这样代码生成阶 段可以认为它的输入是正确的。
对于目标机器,如果有多个寄存器可供目标程序运行时使用,在编
译时根应据采访用问合内理存的数方来法定分义配每这条些指寄令存的器执。行把代运价算,数则据对存于放以在下寄的存一器中些,操
将作会 有减其少相访应问的内执存行的代次价数: ,从而可以提高执行速度。
分配操中作不码是把寄存器操平作均数分l 配给各个操变作量数使2用,而是从可执用行的代寄价存
② 检查达栈有式无进元行素语。法若没语有义,分则析当。前运算符入栈,然后转回到步骤①检查下一 个栈为,符了然号;后处否转理回则简步检单骤查起①运算,见符否,则的规转优定到先被步级。骤处③若理。当的前简的单比栈表顶达的式大的,前则当后前都运有算符入 特殊符号“#”,即呈下列形式:
③ 检查当前符号和栈顶元素。若当前符号是“)”,而栈顶元素是“(”,则“(”上
8.2.2 虚拟机的汇编指令
1.填地址指令
设有一维数组a: array[m..n]of integer; 其元素有a[m], a[m+1], … , a[i], … , a[n],一共需要n-m+1个存储单元: <a[m]>,<a[m+1]>,…,<a[i]>,…,<a[n]> 一般有存储单元: <a[i]>=<a[m]>+i-m 由于<a[0]>=<a[m]-m,所以有<a[m]>=<a[0]>+m。从而对于一维数组的存储单
器中分出几O个P ,固定分配寄给存几器个简单变量寄使存用器。按照什么标准来l分配呢? 为此引入一个术语:指令寄的存执器行代价,并规内定存访单问元内存一次的代价2为1。
寄存器
寄存器间接地址
2
寄存器
内存间接地址
3
寄存器分配
基于以上原因,分配中尽可能把变量值保留在寄存器中,当一个寄 存器的值不再需要时,该寄存器可以收回留作他用,但这只有对程序做 了全面的数据流分析后才能确定,这种分析要做大量的额外工作不太实 际,在寄存器的分配中常常以基本块为单位进行。
图8.1 目标代码生成过程
目标代码生成程序中的有关问题
对于一个好的代码生成程序,要求其使所生成的目标 代码尽可能地短,并能较充分地发挥目标计算机可用资源的 效率。如充分利用计算机的寄存器或变址器,以节省访问内 存的时间,尽可能使用执行速度较快的指令,存储管理合理, 等等。
熟悉目标机器和它的指令系统是设计一个好的代码生 成程序的先决条件,但在代码生成的一般性讨论中,不能对 目标机器细节描述到足够详细的程度,以便对一个完整的语 言产生好的代码。本章不以某一台具体的计算机做背景,只 是针对一个假想的计算机模型——虚拟机给出生成目标代码 的算法。
下面就以一种虚拟机指令系统来讨论目标代码的生成。
寄存器分配
通常,计算机存储之间不直接打交道,而是通过寄存器。寄存器可 以用来保存中间计算结果,而且运算对象在寄存器中的指令一般比运算 对象在内存的指令短些,执行速度也快些,因此,充分利用寄存器对生 成高质量目标代码尤其重要。寄存器的分配也自然成为目标代码生成中 的主要问题。
运行时的存储管理
通常所见到的计算机都是冯·诺依曼体系结构的,其特征是 变量——存储字,即用变量来仿效存储字,变量名实际上是 存储字的名字。
对于编译程序来说,必须对源程序中出现的变量与常量分 配运行时刻的存储空间,这一工作由先行端的分析和代码生 成程序共同完成。另从符号表的信息可以确定一个名字(标识 符)所代表数据对象在过程数据区中的相对地址。为了存储分 配的正确实现,除了必须考虑标识符的作用域问题外,还必 须考虑字边界对齐问题,即对于字节编址的计算机,必须注 意对于不同类型的量所分配存储区域的起始地址都必须符合 边界要求。
8.3.1 从逆波兰表生成目标代码
例如,对于逆波兰表达式:
所生成的目标代码为
ห้องสมุดไป่ตู้ab*cd+e/-
CLA a MPY b
具体生成目标代码处理过程如表8.2所示ST。O M
CLA c
ADD d
DIV e
SUB M
8.3.1 从逆波兰表生成目标代码
又如,对于逆波兰表达式:
所生成的目标代码为
aQbc*-d/
下推入栈,最后转回步骤②重复处理当前运算符。另外,在形成目标代码时 应注意“AC”有无被别的量占用,并进行相应处理。
元,有公式: <a[i]>=<a[0]>+i 其中<a[0]>称为数组的假头,<a[m]>称为数组的真头。
8.2.2 虚拟机的汇编指令
例如,有赋值语句:
X:=a[10]
设数组a为单块连续存放方式存放,<a[0]>为假头,则<a[10]>=<a[0]>+10。
假设指令编号从100开始,于是汇编指令编写的程序为 100 CLA <10> 101 ADD <<a[0]>> 102 STA l03 103 CLA 104 STO X
在一个基本块内,按照中间语言代码的顺序,逐个基本块产生目标 代码。生成的目标代码应尽可能将运算的结果存放在寄存器中,直到该 寄存器必须用来存放别的变量或基本块结束为止。这样可以减少基本块 访问内存的次数,从而提高运行速度。
如果把寄存器分配给某些变量,则该变量在定值前每引用一次,将 可少访问一次内存,从而可节省执行代价1;如果某变量在基本块中被定 值,且出基本块后还要被引用,则把寄存器固定分配给该变量,可省去 把它保存到内存单元的操作,从而节省执行代价2。
退栈并转#<回简第单一表步达;若式当>#前是“#”,栈项也是“#”,则处理完毕,算法结束。
若不是上述两种情况则转到步骤④。
④ 应根并的据把目与栈标顶“运代运#算”码算视分,符为量然的优后栈性先同质(栈时(数单退)为目,栈0或相和的双栈应运目。算算运运法算符算符如。结)下果从同存。栈时放顶引在取进“一运A个C”或算中两,符个并栈分把(量“栈生A)C成”标相志
SGN a STO M
具体生成目标代码的处理过程如表8.3所CL示A 。b
MPY c
ISU M
DIV d
8.3.1 从逆波兰表生成目标代码
① 从上左面到右首扫先描把简简单单表达表式达中式的改每造个符成号中。间若语扫言描到的运逆算波分兰量形时就式下,推然入栈, 若扫描后到由运逆算波符兰(包表括达括号式和生“成#”目)时标就代转码到②。,实如际此中一也直扫常描把下两去步,合直到整 个表达为式一扫步描,结根束为据止运。算符优先数的大小关系,直接对简单表