《编译原理课程教案》第8章:代码生成
编译原理目标代码生成
中间代码生成
探讨中间代码的概念、作用和生成方法,包括抽象语法 树、三地址代码等中间表示形式。
代码优化
介绍代码优化的原理和常用技术,如常量折叠、复制传 播、死代码删除等,以提高目标代码的质量。
目标代码生成
详细讲解目标代码生成的过程和方法,包括指令选择、 寄存器分配、汇编语言输出等关键步骤,以及针对不同 目标平台的优化策略。
语义分析
介绍了语义分析的基本任务,如类型 检查、控制流分析、数据流分析等。
中间代码生成
阐述了中间代码的概念、作用及生 成方法,包括抽象语法树、三地址 代码等。
目标代码生成
详细讲解了目标代码生成的过程, 包括指令选择、寄存器分配、优化 等。
对未来编译技术的展望
智能化编译技术
随着人工智能技术的发展,未来编译器可能具备更强的自 主学习能力,能够自动优化代码性能、提高编译效率。
01
指令选择算法
根据目标计算机的指令集和程序语义, 选择最合适的指令来实现程序功能。
02
03
代码优化算法
通过对生成的机器相关代码进行各种 优化(如常量折叠、循环展开等), 提高其执行效率和资源利用率。
06
代码生成器的设计与实现
代码生成器的结构
中间代码生成器
将源程序转换为中间代码形式,便于后续优化和代码生成。
2
编译过程包括词法分析、语法分析、语义分析、 中间代码生成、代码优化和目标代码生成等阶段。
3
编译原理不仅关注如何生成高效的目标代码,还 关注如何提高编译器的可维护性、可移植性和可 扩展性。
目标代码生成的意义
提高程序执行效率
目标代码是计算机直接执行的指令,其执行效率远高于高级语言程序。通过优化目标代码 ,可以提高程序的执行速度。
编译原理-语法制导翻译和中间代码生成
例:类型、运算、维数、越界
–语义处理:
执行真正的翻译,生成程序的中间代码目标代码。
–例:变量的存储分配
–例:表达式的求值
–例:语句的翻译(中间代码的生成)
3
8.1 属性文法
• 语义分析的描述 –描述语法规则的同时,编写相应的语义 动作和计算顺序
• 语义的形式化描述 –操作语义学、公理语义学、指称语义学
例8-3 3*5+4 的 语法树与属性计算
T.val=3 F.val=3 digit.lexval=3
E.val=15 T.val=15
*
L Print(19)
E.val=19 +
T.val=4
F.val=4
F.val=5
digit.lexval=4
dgit.lexval=5
19
T.type=real
• 继承属性 – 从其兄弟结点和父结点的属性值计算出来的 – 如:L.in
• 固有属性(单词属性)
17
属性的计算
• 语法制导翻译:作语法分析,构造语法树,然 后在树的每个结点上添加相应的语义规则
• 综合属性 – 自底向上按照语义规则来计算各结点的综合 属性值
• 继承属性 –需要探讨计算次序
18
文法
E→ -E1 E.p:= mknode('-', 0, E1.p)
E→ (E1) E.p:= E1.p E→ id E.p:= mkleaf(id, id.entry) E→ num E.p:=mkleaf(num,num.val)
37
生成后缀式的属性文法
产生式
语义规则
S→id:=E Print( || E.code || “:=”)
8编译原理之讲义代码生成
• RISC、CISC; • 可重定向代码、汇编语言
代码生成器设计中的问题
指令选择
•代码生成器将中间表示形式映射为目标
机代码
•映射的复杂性由下列因素决定:
• IR的层次
• 高:用代码模板翻译,但代码质量不高,需优 化
• 低:利用低层次细节生成更高效的代码
• 指令集体系结构本身的特性 • 期望的目标代码质量
方便记忆)
常量#constant56
例子
x=y-z
• LD R1, y
//R1=y
• LD R2, z
//R2=x
• SUB R1, R1, R2
//R1=R1-R2
• ST x, R1
//x=R1
b=a[i]
• LR R1, i
//R1=i
• MUL R1, R1, 8
//R1=R1*8
• LD R2, a(R1)
8.1代码生成器设计中的问题
设计目标:
• 生成代码的正确性(最重要) • 易于实现、测试和维护
输入 输出 指令选择 寄存器分配 计算顺序
代码生成器设计中的问题
输入
• 前端生成的源代码的IR(中间表示形式)及符号表信息 • 中间表示形式的选择
• 四元式、三元式、字节代码、堆栈机代码、后缀表示、抽象 语法树、DAG图、…
//R2=contents(a+contents(R1))
• ST b, R2
//b = R2
程序及指令代价
不同的目的有不同的度量
• 最短编译时间、目标程序大小、运行时间、能耗
不可判定一个目标程序是否最优 指令代价=指令固定代价(设为1)+运算分量寻
址模式代价,例:
编译原理代码生成(new)
+ +
:=
:=
i +i +
i 1i 1
:= i+
i1
例题2
按一般的代码生成,i = i +1的计算结果保留在 寄存器中,因此这三个i = i +1的计算次序不会 影响最终的结果。结果应该是6。
结果是7的话,一定是 某个i = i +1的结果未保 留在寄存器中。上层 计算对它的引用落在 计算另一个i = i +1的 后面
的寄存器R(可能产生MOV R,M指令,并修改 M的描述 ) 否则,如果x在基本块中不再引用,或者找不到适当的被占用寄存器,选择x的内存单
元作为L。
11.4 一个简单的代码生成器
赋值语句d := (a b) + (a c) + (a c)
编译产生三地址语句序列: t1 := a b t2 := a c t3 := t1 + t2 d := t3 + t2
代码生成
本章内容 一个简单的代码生成算法 涉及存储管理,指令选择,寄存器分配和计算次序选择等基本问题
源程序
中间 前端 代码
代 码 中间 代码
优 化 代码 生成
器
器
目标 程序
11.1 代码生成器的设计中的问题
11.1.1 目标程序 可执行目标模块 可重定位目标模块
允许程序模块分别编译 调用其它先前编译好的程序模块
+ +
:=
:=
i +i +
i 1i 1
:= i+
i1
例题2
如果机器有INC指令的话,编译器极可能产生一条INC指令来完成i = i +1 X86/Linux机器上果真是这么做的
编译原理教案
编译原理教案说明:一、参考书:1、陈意云、张昱:《编译原理》,高等教育出版社,2003年。
2、陈意云、张昱:《编译原理习题精选》,中国科技大学出版社,2003年。
3、吕映芝、张素琴、蒋维杜:《编译原理》,清华大学出版社,1998年第二版。
4、王生原、吕映芝、张素琴:《编译原理课程辅导》,清华大学出版社,2007年。
5、伍春香:《编译原理习题与解析》,清华大学出版社,2001年。
6、Andrew W.Appel:《现代编译原理—C语言描述》,人民邮电出版社,2005年。
7、Noam Nison等:《计算机系统要素》,电子工业出版社,2007年。
8、Randall Hyde:《编程卓越之道(第二卷)》,电子工业出版社,2007年。
二、教学目的:通过学习形式语言与自动机理论、词法分析、语法分析、语义分析、代码优化和生成等内容使学生掌握构造编译程序的基本原理和基本方法,并通过上机实习使学生进一步掌握开发应用程序的基本方法,为深入理解计算机系统、程序设计语言与开发大型应用程序打下良好的基础。
三、教学时数:课堂教学51学时,上机实验30学时。
四、授课内容:第一章编译程序概述第二章 PL/0编译程序的实现第三章文法和语言第四章词法分析第五章自顶向下语法分析方法第六章自底向上优先分析方法第七章 LR分析方法第八章语法制导翻译和中间代码生成第九章符号表第一○章目标程序运行时的存储组织第一一章代码优化第一二章代码生成第一章概述一、说明:1、教学目的与要求:了解编译程序的概念、结构以及工作流程。
2、主要内容:什么是编译程序、编译过程概述、编译程序的结构、编译阶段的组合、编译技术和软件工具以及实例分析。
3、教学重点:编译程序的结构以及每一阶段的任务。
4、教学难点:理解编译程序各模块的判错功能、编译方式和解释方式执行速度上的不同。
二、教学内容第一节编译程序1、机器语言:直接用计算机能够识别的二进制代码指令来编写程序的语言。
编译原理 第八章 中间代码生成
第八章中间代码生成编译器分为单遍扫描和多遍扫描两大类。
其中单遍扫描编译器是从源代码直接生成目标代码;而多遍扫描编译器是首先生成中间代码,然后再从中间代码生成目标代码。
其中从源代码生成中间代码的部分称之为编译器的前端,而从中间代码生成目标代码的部分称为编译器的后端。
中间代码在何时产生,没有固定模式。
比较好的模式也许是在语义分析时产生中间代码。
在这种模式下语义分析的主要功能可归结为以下三点:■进行语义检查■在扫描声明部分时构造标识符的符号表(此时过程声明和动态数组声明也产生中间代码)■在扫描语句部分时产生中间代码为了教学上的方便,在第六章里介绍语义分析时只介绍了上述三项中的前二项,因此,在此作为独立的一章将专门介绍中间代码的生成原理,而且省略所有的语义检查,但语义信息仍将产生。
中间代码生成器的输入是语义分析之前的还是之后的,对于我们的讨论来说是无关紧要的。
8.1 中间语言8.1.1 中间语言设置与直接代码生成由于各种原因,在编译器历史中用到了各种形式的中间代码结构。
使用中间代码的主要好处是:便于移植,便于修改,便于优化,便于掌握;直接生成目标代码的主要好处是,可避免重复性工作,从而可减小编译器的体积。
如果对于优化没有太大的要求,那么直接生成目标代码是比较合适的。
由于词法分析到中间代码生成部分不依赖于具体机器,因此易于移植,只需要修改目标代码的生成部分。
编译器都进行不同级别的优化,其优化过程实际上是对程序的操作过程,而要对程序进行操作必须首先把程序文本转换成某种类型的数据,而中间代码正是程序的一种数据结构表示,因此中间代码的生成将给优化代来极大方便。
通常见到的中间代码有:后缀式(栈式)中间代码,三地址中间代码(三元式和四元式),图结构中间代码(树,DAG)。
其中后缀式中间代码是最早期使用过的一种中间代码,现在很少见到,主要用到的是后两种,它们各有特点,很难说哪种的绝对好。
8.1.2 栈式中间代码—后缀式栈式中间代码是适合于栈式机的一种中间代码,通常称之为后缀式。
编译原理之代码生成
03
04
05
1. 语法分析:根据语言 2. 语义分析:对抽象语
的语法规则,将源程序 法树进行语义检查和处
解析成抽象语法树
理,包括类型检查、符
(Abstract Syntax Tree,号表管理等。
AST)。
3. 中间代码生成:根据 抽象语法树和语义分析 结果,生成中间代码。 常见的中间代码形式有 三地址码、静态单赋值 形式(Static Single Assignment,SSA)等。
运行时系统自动管理程序中的内存资源, 通过垃圾回收机制回收不再使用的内存空 间,防止内存泄漏和野指针等问题。
运行时系统对程序性能的影响和优化
性能影响
运行时系统的设计和实现会直接影响程序的性能。例如,垃圾回收算法的选择和实现会 影响内存的回收效率和程序的暂停时间。线程调度策略的选择也会影响程序的并发性能
编译原理是计算机科学的重要分支,对于理解计算机如何执行程序以及如何提高程 序执行效率具有重要意义。
代码生成在编译过程中的作用
代码生成是编译过程的最后阶段, 负责将中间代码或优化后的代码 转换为目标机器上的可执行代码。
代码生成器需要了解目标机器的 指令集、寄存器分配、内存管理 等相关知识,以生成高效且正确
中间代码在编译器中的 作用主要有以下几点
使得编译过程分为相对 独立的前端和后端,降 低了编译器的复杂性。
提供了统一的中间表示, 便于实现不同语言之间 有利于进行各种优化操 的互操作性。 作。
ห้องสมุดไป่ตู้
中间代码生成的算法和步骤
01
02
中间代码生成的主要算 法包括语法分析、语义 分析和中间代码生成三 个步骤。
具体步骤如下
代码生成器的测试和评估方法
编译原理compiler8
9
1. 二、寄存器描述和地址描述 2. 为了在代码生成中充分利用寄存器,生成尽
可能简短的代码,将使用寄存器描述数组 RVALUE[Ri]记录寄存器Ri是空闲着,还是已 分配给某些变量;将使用变量地址描述数组 AVALUE[A]来记录变量A的现行值是存放在 某个寄存器中,还是在某个主存单元中,或 者既在寄存器中又在主存中。
12
(3) 如果已无可用寄存器,即寄存器均被占用,则
只能从被占用的寄存器中选择一个Ri,先将占用 Ri的变量的现行值逐一保存到主存单元中,然后
才可分配给x使用。选择标准是使这种复制副本的
代价尽可能小,很明显,最好选在主存单元中已
副本的占用Ri的变量,或者该变量在P以后不再被 引用或在最远的将来才会被引用。在这种情况下,
op Ri, c(Rj) (Ri)op((Ri)+c) Ri (4) 间接型:
op Ri, *Rj (Ri)op((Rj)) Ri
op Ri, *M (Ri)op((M)) Ri
op Ri, *c(Rj) (Ri)op(((Ri)+c)) Ri 5
其他操作指令的指令意义为:
(1) MOV Ri, M (2) MOV M, Ri
3
二、目标机器 目标机多种多样,要生成好的目标代码,必须熟悉目标机, 特别是指令系统的细节。作为一般讨论,我们不打算讨论 十分依赖于目标机器细节的内容,当然也不可能生成完整 的满意的代码。我们只讨论一般技术,只针对一种抽象的 目标机,它是简单的,作为各种目标机的子集,它是公共 的。 目标机具有几个通用寄存器,它们同时可作为变址器。
2
第八章 代码生成
§8.1 目标代码
精品课程编译原理-PPT课件第8章 语法制导与中间代码生成
(3)一致性检查。在很多场合要求对象只能被 定义一次。例如Pascal语言规定同一标识符在一 个分程序中只能被说明一次,同一case语句的标 号不能相同,枚举类型的元素不能重复出现等等。
(4)上下文相关性检查。比如,变量名字必 须先声明后引用;
(5)名字的作用域分析。各变量的作用域可 能是不一样的,要通过分析明确各变量的作用域。
例子: a:=b*c+b*d的相应三元组
①(*, b, c) b*c ②(*, b, d) b*d ③(+, (1),(2)) b*c+b*d ④(:=,(3), a) a:=b*c+b*d
例子:tri(A*B+C) =tri(A*B)||tri(c)||2:(+,①≥,C) =1:(*, A,B) A*B 2:(+,①,C) A*B+C
8.2.2 S-属性文法和自下而上翻译 一般的属性文法的翻译器很难建立,然 而L-属性文法的翻译器很容易建立。
L-属性文法的一个特例叫S-属性文法。 S-属性文法是只含有综合属性的属性文法。
8.2.3 L-属性文法在自下而上分析中实现 L-属性文法允许一次遍历就计算出所以的 属性值。
8.3 中间代码的形式
3*5+6的带注释的分析树
只使用综合属性.
T.val=3 F.val=3
E.val=15 T.val=15
*
L
E.val=21
+
F.val=5
T.val=6 F.val=6 digit.lexval=6
digit.lexval=5
digit.lexval=3
3*5+6的带注释的分析树
继承属性
一个结点的继承属性值是由此结点的父结点和/或兄弟结 点的某些属性来决定的。
编译原理与代码生成
编译原理与代码生成在计算机科学领域中,编译原理是一门研究编程语言如何被转换成可执行代码的学科。
它涉及到编译器的设计和开发,其中一个关键环节就是代码生成。
代码生成是编译过程中的最后一步,它将中间表示形式(如抽象语法树或中间代码)转化为机器代码或目标代码,以便计算机能够直接执行。
代码生成是编译过程中至关重要的一环,其质量直接影响到最终生成的可执行程序的效率和性能。
一个好的代码生成器应该能够生成高效、优化的机器代码,并且具备可移植性。
为了实现这一目标,代码生成器通常需要考虑以下几个方面:1. 寄存器分配:寄存器是现代计算机体系结构中重要的资源之一。
在代码生成阶段,寄存器的分配是一个关键问题。
代码生成器需要决定哪些变量应该存储在寄存器中,哪些应该存储在内存中,并生成相应的指令来进行寄存器的分配和管理。
2. 内存分配:除了寄存器分配外,代码生成器还需要考虑如何进行内存分配。
它需要决定哪些变量应该存储在堆栈上,哪些应该存储在静态数据区,以及如何有效地进行内存的分配和释放。
3. 代码优化:代码生成器还需要考虑一系列的代码优化技术,以提高生成代码的质量和效率。
这些技术包括常见的优化方法,如常量折叠、公共子表达式消除、死代码删除等。
通过应用这些优化技术,代码生成器可以生成更紧凑、更高效的机器代码。
4. 目标代码生成:最后,代码生成器需要将中间表示形式翻译成目标机器能够执行的机器代码。
这一过程中,代码生成器需要根据目标机器的指令集架构和特性来生成相应的机器代码。
同时,为了保证生成的代码的可移植性,代码生成器还需要考虑各种编译器选项和标准。
综上所述,编译原理中的代码生成是编译过程中不可或缺的一部分。
通过合理的寄存器分配、内存分配、代码优化和目标代码生成,代码生成器可以生成高质量、高效率的机器代码。
这对于计算机科学学习者和编译器开发者来说,都是一个重要的课题。
只有深入理解编译原理和代码生成的原理和技术,才能够设计和实现出优秀的编译器和程序。
计算机基础知识点编译原理代码生成
计算机基础知识点编译原理代码生成编译原理是计算机科学中非常重要的一门学科,它研究的是如何将高级语言编写的程序转化为机器可以执行的指令序列。
在编译过程中,代码生成是其中一个关键的步骤。
本文将介绍编译原理中的代码生成基础知识点。
一、代码生成概述代码生成是编译过程中的最后一个阶段,它的任务是将中间表示形式(如抽象语法树或中间代码)转化为目标机器代码,使得程序可以在计算机上运行。
代码生成的目标是产生高效且正确的机器指令序列,以最大程度地利用计算机的硬件资源。
代码生成的过程可以分为以下几个步骤:1. 寄存器分配:将变量和临时值分配到计算机的寄存器中,以便在指令中进行操作。
2. 指令选择:根据中间表示的特点和目标机器的指令集,选择适当的机器指令来实现所需的操作。
3. 指令调度:对指令进行重新排序,以减少指令相关性和提高执行效率。
4. 内存分配:将变量和临时值存储到内存中,以便在需要时可以进行访问。
5. 代码优化:对生成的机器指令进行优化,以减少执行时的开销和资源占用。
二、寄存器分配在代码生成的过程中,寄存器分配是一个非常重要的环节。
寄存器是计算机中的一种高速存储设备,可以用于存储和执行指令操作。
在生成的机器代码中,寄存器通常用于存储临时值和计算结果。
寄存器分配的目标是将变量和临时值存储到寄存器中,并进行相应的寄存器的分配和释放。
常见的寄存器分配算法有线性扫描分配算法、图着色分配算法等。
寄存器分配算法的选择通常取决于目标机器的寄存器数量和寄存器之间的互斥关系。
三、指令选择指令选择是代码生成的关键一环,它的任务是根据中间表示和目标机器的指令集,选择合适的机器指令来实现所需的操作。
指令选择的准则通常是从操作数和操作符的角度考虑,以及考虑目标代码的执行效率和可读性。
指令选择的过程中,需要考虑目标机器的指令格式、寻址方式、寄存器约束等因素。
对于一些特殊的操作,如函数调用、跳转指令等,还需要考虑目标代码的控制流程和程序执行的正确性。
编译原理课件-中间代码生成
Wensheng Li BUPT @ 2008
賦值語句a:=b*-c+b*-c的圖表示法
語法樹表示: assign
dag圖形表示: assign
a
+
a
+
*
*
b uminus b uminus
c
c
* b uminus
c
Wensheng Li BUPT @ 2008
7/75
二、三地址代碼
三地址代碼
賦值語句a:=b*-c+b*-c的三元式表示
Wensheng Li BUPT @ 2008
op
arg1
arg2
(14) uminus
c
(15)
*
b
(14)
(16) uminus
c
(17)
*
b
(16)
(18)
+
(15)
(17)
(19)
:=
a
(18)
12/75
語句x[i]:=y和x:=y[i]的三元式序列
EE1+E2
{ E.place=newtemp; if (E1.type==integer) && (E2.type==integer) { emit(E.place := E1.place + E2.place); E.type=integer; }; else if (E1.type==real) && (E2.type==real) { emit(E.place := E1.place real+ E2.place); E.type=real; }; else if (E1.type==integer) && (E2.type==real) { u=newtemp; emit(u := inttoreal E1.place); emit(E.place := u real+ E2.place); E.type=real; }; else if (E1.type==real) && (E2.type==integer) { u=newtemp; emit(u := inttoreal E2.pace); emit(E.place := E1.place real+ u); E.type=real; }; else E.type=type_error; }
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
寄存器分配
• 利用寄存器放置运算对象的指令比运算对象 在内存中的指令短些 • 执行也快些 • 充分利用寄存器对高质量的代码生成是重要 的 寄存器
寄存器优化 局部性优化 片内CACHE
片外CACHE
主存 外存
寄存器分配(续)
• 寄存器使用的两个子问题 – 寄存器分配:为程序的某一点选择驻留在 寄存器中的一组变量 – 寄存器指派:挑出变量将要驻留的具体寄 存器 • 寄存器分配的最优化是NP完全的 – 特定要求的满足
注意:上述次序不能颠倒,因为x可能是y或z 例:t := a – b u := a –c v := t + u d := v + u
下次引用信息(3)
(1)t:=a+b
{t:3,活; a:2,活;b:无,活}
名字 t a 下次引用 无 3 无 无 2 1 活跃 非 活 非 活 活 活
(2)u:=a-c {u:3,活;a,c:无,活} (3)v:=t+u {u,v:4,活;t:无,非} (4)d:=v+u {d,u,v:无,活}
• 各种代码的形式
– 中间代码: 后缀式,三地址代码 – 符号表中的项:名字,类型,嵌套深度,偏移量 – 目标代码:绝对机器代码,可重定位代码,汇编
• 代码生成器的输出必须是正确和高质量的 • 产生最优化代码的问题是不可判定的
8.2 代码生成器设计中的问题
• 代码生成器依赖于目标机器和操作系统 • 要充分发挥目标机器的能力:充分利用目 标机器的资源 • 代码生成器固有的问题
存储管理
• 把程序中的名字映射到运行时的目标对象的地址是 由前端和代码生成器共同完成的 – 语言中过程的语义决定了运行时刻名字如何与存 储空间相联系 – 对名字的引用通过符号表 • 记录了名字在过程数据区的相对地址 • 所需要的存储空间 – 运行时活动记录的管理 • 运行时活动记录的分配和释放作为过程调用 和返回序列的一部分 – call(调用),return(返回) – halt(暂停),其它语句 – 存储管理分静态、栈式和堆式存储分配
56: 60:
j
84:
n
指令地址的决定
• 通过一个计数器决定每个指令的地址 • 标号的处理:j: goto i /*j是当前语句的号码*/
– 如果i小于j
• i出现在j之前,目标地址是i对应的三地址代码的第一 条指令地址
– 如果i大于j
• i出现在j之后,目标地址此时不可知,可以利用回填 的技术解决
指令选择
• 目标机器指令系统的性质决定了指令选择的难 易程度 • 指令系统的一致性和完整性是重要因素
– 如果目标机器不能以一致的方式支持各种数据类型, 则每种例外都要专门的处理
• 指令的速度和机器的特点是另一些重要的因素
– 如果不考虑目标程序的效率,则指令选择是直截了当 的
• 代码的质量取决于它的执行速度和长度
– 模式匹配是自顶向下的推导,而表驱动可以是自顶 向下的推导,也可以是自底向上的归约 – 模式匹配依次匹配一个模版,模版集易于检查正确 性,容易估计空间复杂性 – 表驱动试图找出所有匹配的语法规则,代价高,语 义动作表不容易检查正确性,大小也不容易估计
下次引用信息(1)
• 下次不再引用意味着优化的机会 – 寄存器优化 – 临时名字存储单元的指派 计算下次引用信息 – 三地址语句中名字的引用定义:
– – 寄存器地址模式的代价为0 含内存单元和常量的地址模式的代价是1,因为这 种运算对象必须和指令存在一起
•
•
如果空间至关重要,则应使指令的长度极小化 – 节省取指的开销,从而减少指令的执行时间 – 提高指令cache的使用效率 指令代价的例子
– – – – MOV R0 , R1 代价是1,只占一个内存字 MOV R5 , M 代价是2,M要独占一个内存字 ADD #1 , R3 代价是2,常数1占一个内存字 SUB 4(R0) , *12(R1) 代价是3,4和12各占一个内 存字
•
– 在基本块内:下次引用信息
• • •
下次引用信息(2)
• 对每一个三地址语句 i:x := y op z,依次执行 下述步骤: – 把当前符号表中变量x的下次引用信息和活跃信 息附加到语句i上;(如果x不活跃,则这个语句 可以删掉) – 把符号表中x的下次引用信息和活跃信息分别置 为“无下次引用”和“非活跃”; – 把当前符号表中变量y和z的下次引用信息和活 跃信息附加到语句i上; – 把符号表中y和z的下次引用信息均置为i,活跃 信息均置为“活跃”。
b
c d u v
1 无 2 无
活 活 活 活
无 无
无 4 3 无 无 4 无
非 活
活 活 活 非 活 活 非
Hale Waihona Puke 下次引用信息(4)• 临时名字的存储分配
– 在需要临时变量时申请一个新的名字是简单 有效的,但浪费空间 – 如果两个临时变量不是同时活跃的,则可以 压缩在同一单元中 – 临时变量存储单元的分配:
• • 找到第一个不含活跃临时变量的单元,指派给待 分配的临时变量 如果没有合适的单元,则在活动记录的临时变量 区域中增加一个单元
下次引用信息(5)
例子: t1 := a * a t2 := a * b t3 := 2 * t2 t4 := t1 + t3 t5 := b * b t6 := t4 + t5
• 假定三地址语句i把a的值赋给x,如果语句j用x作为运算 对象,并且控制从i流到j,这条路径中没有x的其它赋值, 则称j引用x在I定的值 对每个基本块反向扫描 为每个名字x在符号表中登记它是否在本块中有下次引 用 如果在本块中没有下次引用,则登记它是否在本块的出 口活跃。缺省认为所有的非临时变量在出口都是活跃的
t1 := a * a t2 := a * b t2 := 2 * t2 t1 := t1 + t2 t2 := b * b t1 := t1 + t2
简单的代码生成器(1)
• 假设条件:
– 三地址语句的每种算符都有对应的目标机器 算符 – 计算结果留在寄存器中尽可能长的时间。只 有在以下两种情况才把它存入主存 • 此寄存器要用于其它计算 • 正好在过程调用、转移或标号语句之前
– 可以从多种指令中选择合适的:a=a+1
MOV ADD MOV a , R0 #1 , R0 R0 , a
INC
a
– 时间信息对代码序列是重要的,但不是任何时候都精 确的
指令选择的例子
• 逐条语句地产生代码的方法常常产生低质量 的代码
MOV a := b+c d := a+e
b
, R0 ADD
c
一个代码生成器的输入
其中,arr,i,j是过程s中定义的数据;buf和n是过 程p定义的数据
三地址代码 /*s的代码 */ action1 S的活动记录
0: 返回地址
p的活动记录
0: 返回地址
call p
action2 halt /*p的代码 */ action3 return
4:
arr i
4:
buf
源程序 机器描述 前端 预处理器 中间语言 模版集 模式匹配 目标语言
•
代码生成器的产生方法(续)
• 表驱动代码生成
– 把输入的表达式树看成是一个输入串 – 通过构造状态转换表,用一系列语义动作完成 代码生成
源程序 机器描述 前端 预处理器 中间语言 状态转换 目标语言
语义动作表
•
表驱动和模式匹配的差别
8.3 目标机器
• 熟悉目标机器和它的指令系统 – 是设计一个好的代码生成器的先决条件 – 但不存在通用的有效的机器描述 • 目标机器 – 字节可寻址机器,4字节为一个字 – 有n个通用寄存器R0 , R1 , … , R(n-1) – 指令形式:
• 机器指令形式(op destination, source) ADD Rd,Rs // d+s SUB Rd,Rs //d-s MOV Rd,Rs //s d • 机器指令开销 (cost),不同的操作开销不同 MOV R,M 开销 2 ADD #1 ,R 开销 2 MOV R0,R1 开销 1
目标机器的地址方式
地址方式
直接地址方式 寄存器方式 间接寄存器方式 索引方式
汇编形式
M R *R c(R) *c(R)
地址
M R contents(R) c+contents(R) contents(c+contents(R))
增加的开销
1 0 0 1 1
间接索引方式
指令代价
• 如果把指令代价取成1,加上上述的地址模式的 附加代价,就是对应指令的长度(以字计算)
例:a:=b+c 的实现方式: 1. MOV R0 , b ADD R0 , c MOV a , R0 代价=6 2. MOV a , b ADD a, c 代价=6 假定R0, R1和R2中分别存放a, b和c的地址, 采用: 3. MOV *R0, *R1 ADD *R0, *R2 代价=2 假定R1和R2中分别包含b和c的值, 并且b的值在 这个赋值以后不再需要, 则还可有 4. ADD R1, R2 MOV a, R1 代价=3 要产生好的代码,必须有效利用它的寻址能力,尽 量把名字的左值或右值保存在寄存器中,以便在不 久的将来使用
8.4 代码生成器的产生方法
• 解释型代码生成 – 定义一个虚拟的目标机,编译器的前端直接把源语 言映射到虚拟的目标机语言 – 重用性差,代码生成器紧密地与汇编语言绑定在一 起,对每一种机器要重新写后端代码生成 模式匹配代码生成 – 用机器描述语言描述了中间语言到汇编语言的映射, 预处理器将它转换成相应的模式集 – 代码生成器将中间语言与模式集作匹配,生成汇编 代码 – 匹配算法与机器无关