优化和目标代码生成(PPT课件)
合集下载
第十一章目标代码生成
码中访问存储单元的次数。
这两个问题直接影响代码的执行速度。
基本问题
➢代码生成器的输入 ➢目标程序 ➢选择适当的代码指令 ➢寄存器分配方案 ➢计算顺序
例:考察下面中间代码序列 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和存
第十二章并行编译基础
目标代码一般有以下三种形式:
这两个问题直接影响代码的执行速度。
基本问题
➢代码生成器的输入 ➢目标程序 ➢选择适当的代码指令 ➢寄存器分配方案 ➢计算顺序
例:考察下面中间代码序列 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和存
第十二章并行编译基础
目标代码一般有以下三种形式:
Java代码优化ppt课件
reference类型在32位系统上每个占用4bytes, 在64位系 统上每个占用8bytes。
19
100个entries,cost 8.6k, 实际的double=2*100*8=1.6k
20
Double=16+8,double=8,67%额外开销
21
TreeMap包括: 48bytes+n*40bytes(entry)
Cache分类实现 1.AlarmCache封装了活动告警/1409/清除告警的插入,活动放7天,清除3小时
2.AlarmCFPCache<cfp,fpList>增加本地缓存,提升查询效率
14
DW缓存优化 封装完,代码量依然很大,而且日志不方便,继续拆分:
1.alarmService负责写操作,cacheService负责读操作
3、找到优化的关键点
这是有效优化的关键。找到项目中与你的目标(性能、资源或其他)相背的地方,并将你的努力和 时间用在那里。举一个典型的例子,一个Web项目速度比较慢,开发者在优化时将大部分精力放在了数 据库优化上,最终发现真正的问题是网络连接慢。另外,不要分心于容易实现的问题。这些问题尽管很 容易解决,但可能不是必要的,或与你的目标不相符。容易优化并不意味着值得你花费工夫。
2、选择正确的优化指标
选择正确的指标,是优化的一个重要组成部分,你需要按照这些指标来测量优化工作的进展情况。如 果指标选择不恰当,或者完全错误,你所做的努力有可能白费了。即使指标正确,也必须有一些辨别。 在某些情况下,将最多的努力投入到运行消耗时间最多的那部分代码中,这是实用的策略。但也要记住, Unix/Linux内核的大部分时间花费在了空循环上。需要注意的是,如果你轻易选择了一个很容易达到的 指标,这作用不大,因为没有真正解决问题。你有必要选择一个更复杂的、更接近你的目标的指标。
《哈工大编译原理》课件
词法分析过程
输入
源程序的字符流。
01
输出
源程序的标记流。
02
1. 初始化
设置初始状态和缓冲区。
03
2. 循环
04 从缓冲区中取出一个字符,根
据当前状态和该字符确定下一 个状态和标记。
3. 输出
05 输出当前标记,并更新状态和
缓冲区。
4. 结束条件
06 当缓冲区为空且所有字符都被
处理时,结束词法分析。
三地址代码的生成
三地址代码定义
三地址代码是一种中间代码形式,由一系列的三元式组成,每个三 元式包含三个操作数和两个操作符。
三地址代码的特点
三地址代码具有简单、直观和易于优化的特点,能够清晰地表示程 序中的控制流程和数据流。
三地址代码的生成算法
常见的三地址代码生成算法包括递归下降分析法和语法制导翻译法 。
示中间代码,以便进行有效的优化和转换。
代码生成器的构造
代码生成器通常由指令选择、控制流优化、循环优化等 模块组成。
控制流优化模块负责对控制流进行分析和优化,如消除 冗余计算、消除无用代码等。
指令选择模块负责从中间代码中选择合适的机器指令, 并进行指令调度和并行化。
循环优化模块负责对循环结构进行优化,如循环展开、 循环合并等。
编译原理的应用非常广泛,如编译器设计、程序分析、软件工程等。
编译过程的基本概念
源程序
用高级语言编写的程序,也称为源代码。
目标程序
编译后的程序,也称为目标代码或机器代码。
编译程序
将源程序翻译成目标程序的软件。
编译过程
将源程序通过词法分析、语法分析、语义分析、中间代码生成、优化 、目标代码生成等阶段,最终生成目标程序的过程。
第九章 目标代码生成(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 ))
第七章目标代码生成
if (m>=n) a=a+1; else
上述四元式代码序列G可翻译为 (1) MOV AX, B (2) ADD AX, C (3) MOV T1, AX (4) MOV AX, T1 (5) MUL AX, D (6) MOV T2, AX (7) MOV AX, T2 (8) ADD AX, E (9) MOV A, AX
从正确性来看,这种翻译不存在问题,但却存在冗余。 指令序列中的(4)和(7)两条指令是多余的;而T1、T2均是中间 代码生成时产生的临时变量,它们在出了基本块后将不再使 用,故(3)、(6)两条指令也可删去。因此,在考虑了效率和充 分使用寄存器之后,应生成如下代码:
如一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均为数据区的内存变量。
① 如果AVALUE [Ri]中不包含M,则生成目标代码MOV M, Ri ;
② 当M不是A时,如果M是B或者M是C且同时B也在
RVALUE [Ri]中,则令AVALUE [M]={M,R},否则令AVALUE [M]={M};
③ 删除RVALUE [Ri]中的M。 (4) 给出R,返回。
例7.2 对例7.1,假设只有AX和BX是可用寄存器,用代码 生成算法生成目标代码及其相应的RVALUE和AVALUE。
(1) 首先将基本块中各变量的符号表的待用信息栏置为 “非待用”,对活跃信息栏则根据该变量在基本块出口之后是 否活跃而将该栏中的信息置为“活跃”或“非活跃”。
上述四元式代码序列G可翻译为 (1) MOV AX, B (2) ADD AX, C (3) MOV T1, AX (4) MOV AX, T1 (5) MUL AX, D (6) MOV T2, AX (7) MOV AX, T2 (8) ADD AX, E (9) MOV A, AX
从正确性来看,这种翻译不存在问题,但却存在冗余。 指令序列中的(4)和(7)两条指令是多余的;而T1、T2均是中间 代码生成时产生的临时变量,它们在出了基本块后将不再使 用,故(3)、(6)两条指令也可删去。因此,在考虑了效率和充 分使用寄存器之后,应生成如下代码:
如一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均为数据区的内存变量。
① 如果AVALUE [Ri]中不包含M,则生成目标代码MOV M, Ri ;
② 当M不是A时,如果M是B或者M是C且同时B也在
RVALUE [Ri]中,则令AVALUE [M]={M,R},否则令AVALUE [M]={M};
③ 删除RVALUE [Ri]中的M。 (4) 给出R,返回。
例7.2 对例7.1,假设只有AX和BX是可用寄存器,用代码 生成算法生成目标代码及其相应的RVALUE和AVALUE。
(1) 首先将基本块中各变量的符号表的待用信息栏置为 “非待用”,对活跃信息栏则根据该变量在基本块出口之后是 否活跃而将该栏中的信息置为“活跃”或“非活跃”。
第11章 目标代码生成
– 及时腾空:在离开基本块时,把存在寄存器 中的现行的值放到主存中。
19Байду номын сангаас
11.3.1 待用信息
• 如果在一个基本块内,四元式i对A定值, 四元式j要引用A值,而从i到j之间没有A 的其他定值,那么,我们称j是四元式i的 变量A的待用信息。(即下一个引用点) i: A:=B op C … j: D:=A op E • 假设在变量的符号表登记项中含有记录 待用信息和活跃信息的栏。
32
1 尽可能用B所在的寄存器 2 后尽可能用空闲寄存器 3 抢占用非空闲寄存器
3 从已分配的寄存器中选取一个Ri为所需要的寄 存器R。最好使得Ri满足一下条件: 占用Ri 的变量的值也同时存放在该变量的贮存 单元中,或者在基本块中要在最远的将来才会 引用到或不会引用到。 要不要为Ri中的变量生成存数指令?
31
• 寄存器分配:GETREG(i: A:=B op C) 返 回一个用来存放A的值的寄存器
1 尽可能用B独占的寄存器 2 后尽可能用空闲寄存器 3 抢占用非空闲寄存器 1 如 果 B 的 现 行 值 在 某 个 寄 存 器 Ri 中 , RVALUE[Ri]中只包含B,此外,或者B与A是 同一个标识符,或者B的现行值在执行四元式 A:=B op C之后不会再引用,则选取Ri为所需 要的寄存器R,并转4; 2 如果有尚未分配的寄存器,则从中选取一个Ri 为所需要的寄存器R,并转4;
1
源程序 表 词法分析器 单词符号 语法分析器 语法单位 出
格
错
管
中间代码生成器 四元式 优化段 四元式 目标代码生成器 目标代码
处
理
理
2
• 代码生成是把语法分析后或优化后的中间 代码变换成目标代码。
编译原理之代码生成
03
04
05
1. 语法分析:根据语言 2. 语义分析:对抽象语
的语法规则,将源程序 法树进行语义检查和处
解析成抽象语法树
理,包括类型检查、符
(Abstract Syntax Tree,号表管理等。
AST)。
3. 中间代码生成:根据 抽象语法树和语义分析 结果,生成中间代码。 常见的中间代码形式有 三地址码、静态单赋值 形式(Static Single Assignment,SSA)等。
运行时系统自动管理程序中的内存资源, 通过垃圾回收机制回收不再使用的内存空 间,防止内存泄漏和野指针等问题。
运行时系统对程序性能的影响和优化
性能影响
运行时系统的设计和实现会直接影响程序的性能。例如,垃圾回收算法的选择和实现会 影响内存的回收效率和程序的暂停时间。线程调度策略的选择也会影响程序的并发性能
编译原理是计算机科学的重要分支,对于理解计算机如何执行程序以及如何提高程 序执行效率具有重要意义。
代码生成在编译过程中的作用
代码生成是编译过程的最后阶段, 负责将中间代码或优化后的代码 转换为目标机器上的可执行代码。
代码生成器需要了解目标机器的 指令集、寄存器分配、内存管理 等相关知识,以生成高效且正确
中间代码在编译器中的 作用主要有以下几点
使得编译过程分为相对 独立的前端和后端,降 低了编译器的复杂性。
提供了统一的中间表示, 便于实现不同语言之间 有利于进行各种优化操 的互操作性。 作。
ห้องสมุดไป่ตู้
中间代码生成的算法和步骤
01
02
中间代码生成的主要算 法包括语法分析、语义 分析和中间代码生成三 个步骤。
具体步骤如下
代码生成器的测试和评估方法
目标代码生成
第9章 目标代码生成
编译程序最后一个阶段是目标代码生 成。它通常在语义分析后或者优化后的中 间代码上进行,并将中间代码转化为等价 的目标代码。
本章主要介绍
简单代码生成器的设计和构造方法
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)当到基本块出口时,将变量的值 存放在内存中。
编译程序最后一个阶段是目标代码生 成。它通常在语义分析后或者优化后的中 间代码上进行,并将中间代码转化为等价 的目标代码。
本章主要介绍
简单代码生成器的设计和构造方法
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)当到基本块出口时,将变量的值 存放在内存中。
编译原理第6章代码优化
合并已知量 删除公共子表达式(删除多余的运算)
删除无用赋值
第6部分 代码优化
循环优化
是指对循环中的代码进行优化。
循环优化包括:
代码外提 删除归纳变量 强度削弱
第6部分 代码优化
全局优化
是在整个程序范围内进行的优化, 需 进行数据流分析, 花费代价很高。
第6部分 代码优化
第6部分 代码优化
6.1.2 基本块的DAG表示
DAG(Directed Acyclic Graph)是一种有向图,
常常用来对基本块进行优化。 一个基本块的DAG是一种其结点带有下述标记 或附加信息的DAG:
第6部分 代码优化
(1) 图的叶结点(无后继的结点)以一标识符(变量名)或 常数作为标记,表示该结点代表该变量或常数的值。 如果叶结点用来表示一变量A的地址,则用addr(A) 作为该结点的标记。通常把叶结点上作为标记的标 识符加上下标0,以表示它是该变量的初值。 (2) 图的内部结点(有后继的结点)以一运算符作为标记, 表示该结点代表应用该运算符对其直接后继结点所 代表的值进行运算的结果。 (3) 图中各个结点上可能附加一个或多个标识符,表 示这些变量具有该结点所代表的值。 一个基本块由一个四元式序列组成,且每一个 四元式都可以用相应的DAG结点表示。
(1) G中四元式(2)和(6)都是已知量和已知量的 运算,G'已合并;
(2) G中四元式(5)是一种无用赋值,G'已将它 删除; (3) G中四元式(3)和(7)的R+r是公共子表达 式, G'只对它们计算了一次,即删除了多余的R+r 运算。 因此,G‘是对G实现上述三种优化的结果。
第6部分 代码优化
第6部分 代码优化
SQL优化PPT课件
• 从索引中获取数据,减少了读取的数据块的数量; • 通过索引来实现索引覆盖查询,但前提条件是,查询返回的字段数足够少,
更不用说select *之类的了。毕竟,建立key length过长的索引,始终不是一 件好事情。
2020/2/23
13
二次检索优化
• select sql_no_cache rental_date from t1 where inventory_id<80000; • Using index condition,返回8w条记录, 0.2s • alter table t1 add key(inventory_id,rental_date); • select sql_no_cache rental_date from t1 where inventory_id<80000; • Using index,0.1s
• SELECT * FROM tb_regist r WHERE NOT EXISTS (SELECT regId FROM tb_regist e WHERE e.`regStudentId` = r.regStudentId AND e.`regCreateDate`< r.regCreateDate )
t1.stuId=t2.stuId • 0.145s
• 通过使用覆盖索引查询返回需要的主键,再根据这写主键关联原表获得 • 需要的行,这可以减少mysql扫描那些需要丢弃的行
• 子查询不是魔鬼,当子查询中能命中索引或覆盖索引,且子查询结果集较小时,有奇效 • 覆盖索引在某些场景下,具有意想不到的优化效果。
•
id: 1
பைடு நூலகம்
• select_type: SIMPLE
•
更不用说select *之类的了。毕竟,建立key length过长的索引,始终不是一 件好事情。
2020/2/23
13
二次检索优化
• select sql_no_cache rental_date from t1 where inventory_id<80000; • Using index condition,返回8w条记录, 0.2s • alter table t1 add key(inventory_id,rental_date); • select sql_no_cache rental_date from t1 where inventory_id<80000; • Using index,0.1s
• SELECT * FROM tb_regist r WHERE NOT EXISTS (SELECT regId FROM tb_regist e WHERE e.`regStudentId` = r.regStudentId AND e.`regCreateDate`< r.regCreateDate )
t1.stuId=t2.stuId • 0.145s
• 通过使用覆盖索引查询返回需要的主键,再根据这写主键关联原表获得 • 需要的行,这可以减少mysql扫描那些需要丢弃的行
• 子查询不是魔鬼,当子查询中能命中索引或覆盖索引,且子查询结果集较小时,有奇效 • 覆盖索引在某些场景下,具有意想不到的优化效果。
•
id: 1
பைடு நூலகம்
• select_type: SIMPLE
•
目标代码生成
R R0 VAR MEM
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)
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)。
• 代码优化在整个编译过程的位置
源程序 编译前端 中间代码 中间代码生成 中间代码 目标代码生成 目标程序 中间代码 中间代码优化 目标代码优化
程序员和编译器可能改上程序的位置
源程序 编译前端 中间代码 目标代码生成 目标程序
程序员可以改进 算法,改变循环
编译器可以改进过程调 用、循环和地址计算
编译器可以利用寄存器, 选择指令和窥孔优转换
3
7.1.1 数据类型
类型的合法性检查是判断数据类型是否与上下文的要求一致 数据类型是对该类型数据(变量或常量)的取值是否合法以 及对该类型数据的运算是否合法的一种说明。
4
7.1.2 数据结构
一个程序设计语言如允许使用的数组、记录、字符串、 表、栈等形式的数据结构,在编译程序中应为它们提供相 应的翻译。 为了能对数据结构中的元素进行引用,必须完成从逻辑结 构到能够访问这些数据元素的物理结构的映射。应考虑: 1映射算法相对简单,根据逻辑结构容易计算出物理地址 2从逻辑结构投影到物理结构时,不至于超界或存储溢出 3使用的数据结构承担这种程序设计语言的主要功能 4在这些数据结构定义相关的运算
25
• 指令选择
– 一个编译程序可以看成是一个转换系统,它把源程序转换成 等价的目标代码,也就是说,对源语言种各种语言结构,依 据语义确定相应的目标代码结构,即确定源语言于目标语言 之间的对应关系,确保正确实现语义。显然,能否建立这样 的关系直接影响到编译程序的质量。 – 目标机器指令系统的性质决定了指令选择的难以程度,指令 系统的一致性和完备性直接影响到这种对应关系的建立。如 果目标机器能一致地支持各种数据类型和寻址方式,不需特 别处理例外,这种对应关系的建立就容易得多。 – 指令执行速度和机器特点对产生目标代码的质量也十分重要。 显然,如果指令集合丰富的目标机器对于某种操作可提供集 中处理的时候,应该选择效率高、执行速度快的一种。
26
• 寄存器选择
– 计算机存储单元之间通常都是通过寄存器联系。寄存器可以 保存计算的中间结果,而且运算对象在寄存器的指令一般都 比运算对象在内存的指令要短且运算的快。因此,充分合理 地利用寄存器对生成高质量的代码十分重要。对于寄存器的 使用,应该考虑程序中的哪些变量驻留在寄存器中、驻留多 长时间。进一步,哪个变量驻留在哪个寄存器。这些问题可 以划分成两个子问题: • 在寄存器分配期间,为程序的某一点选择驻留在寄存器中 的一组变量; • 在随后的寄存器指派阶段,选择变量要驻留的具体寄存器。 – 选择最优的寄存器指派方案极其困难,从数学以上讲,这是 一个NP完全问题。如果考虑到目标机器的硬件、操作系统对 寄存器使用的一些要求时,这个问题就变得更加复杂。
• 指令选择 –RISC –CISC • 寄存器分配 –通用寄存器方案 –专用寄存器方案 –大寄存器方案
31
20
7.4 目标代码生成
——具体细节依赖于目标机器和操作系统
• 代码生成在整个编译过程的位置
符号表 语法树
语义分析
中间代码
中间代码生 成与优化 中间代码
中间代码
目标程序
代码生成
21
目标代码的生成
• 输入 –中间代码 • 三元式 • 四元式 • 输出 –可执行代码 –待装配的代码 –汇编代码
22
共同的问题:
编译原理
优化和目标代码生成(2h)
主讲:王海文 2005-2007
第七章
7.1
7.2 7.3 7.4
优化和目标代码生成
编译程序考虑的因素
运行时的内存分配 代码优化
目标代码生成
2
7.1
编译程序考虑的因素
编译程序设计时,除了需要用到前面介绍的 分析技术和制导翻译外,还要考虑如何从源程序 数据空间映射到具体物理存储空间,也就是运行 时的数据表示。 在运行的时候如何组织或存放数据、在源程 序中同名标识符是怎么样描述不同的对象、运行 时的程序控制权是如何转移的,参数是如何传递 以及如何生成质量较高的目标代码都是编译程序 设计者应该考虑的问题。
14
程序的执行效率是可以通过在编译期间进行优化 变换,不改变语义重写程序段以提高程序的工 作效率。虽然变换的方法很多,但是常用的大 致有以下几种: 1 编译求值 2 合并预算 3 删除死码 4 减少频率 5 强度削弱和变换循环条件 6 减少重写传播 15
局部优化: 所需要的开销相对较低,因此从优 化所得到的收益也相对较低。 局部优化只是 在较小的 程序段中进行优化,从而到达优化 的目的。 这种程序段称为基本段。 全局优化: 要产生高效的代码,编译程序仅在 基本块内优化是不够的,编译程序应把程序作 为一个整体来考虑,对各个基本块的信息进行 数据流的分析从而达到优化的目的。全局优化 同局部优化相比,全局优化需要更多的分析, 以便确定优化的可行性。
11
• 设计和实现编译程序代码优化的原则:
(1)等价原则:经过优化后的代码应该保持 程序的输入输出,不应改变程序运行的结果。 (2)有效原则:优化后的代码应该在占用空 间、运行速度这两个方面,或者其中的一个 方面得到改善。 (3)经济原则:代码优化需要占用计算机和 编译程序的资源,代码优化取得的效果应该 超出优化工作所付出的代价。否则,代码优 化就失去了意义。
M R *R c(R) *c(R)
地址
M R contents(R) c+contents(R)
增加的开销
1 0 0 1 1
间接索引方式
contents( c+contents(R))
29
例1:a:=b+c
1. MOV ADD MOV 2. MOV ADD b, R0 c, R0 R0, a b, a c, a cost=6
充分利用寄存器基本块中全局寄存器分配: 不把寄存器平均分配给各个变量使用,而是 从可用的寄存器中分出几个,固定分配给几个变 量单独使用。 标准:以各变量在循环内需要访问主存单元的次 数为标准。 选择计算机指令系统
选择计算次序
23
目标代码的三种形式
地址代真的机器代码 待装配的机器代码模块 汇编语言(宏汇编)
13
优化分类
按阶段分: 与机器无关的优化---对中间代码进行 依赖于机器的优化---对目标代码进行 根据优化所涉及的程序范围分成: (1)局部优化:(基本块)应用于仅包含少量语 句的小程序的优化变换。 (2)循环优化:对循环中的代码进行优化 (3)全局优化:大范围的优化应用于一个程序 单元,如一个函数或一个过程的优化变换。
代码优化
目标: 作为一个高级语言的使用者很希望编译程序所产生 的代码能够和直接用机器语言编写的程序效果一样好。
什么是代码优化
宗旨: 获得较好性能的代码 阶段: source code 用户 front end I.R code generator target code
中间代码 优化
目标代码 优化
10
6
7.1.4 控制结构
一个程序设计语言的控制结构是该语言在程序运行期 间用于改变控制流的语言特征集合。
7
7.2
运行时的内存分配
编译程序需要为源程序中的数据分配执行时的存 储空间,编译程序从操作系统中申请编译程序 计算出的所需的内存,或编译程序生成在运行 时需申请内存的指令。 (1) 确定用来表示某一数据项的内存大小。 (2) 使用适当的内存分配策略,实现具体数 据的作用域和生存期。 (3) 确定以适当的算法访问生存期内的数据, 包括基本型数据结构构造类型。
12
例:
int arr[10000]; void Binky() { int i; for (i=0; i < 10000; i++) arr[i] = 1; } int arr[10000]; void Winky() { register int *p; for (p = arr; p < arr + 10000; p++) *p = 1; }
16
优化的基本方法
• 去处冗余
• 削减强度Hale Waihona Puke • 使用更快的指令17
局部优化
• 方法 –合并已知量 –临时变量改名 –交换语句位置 –代数变换
18
循环优化
• 代码外提 • 强度削弱 –A=A+1 –A++ • 删除归纳变量 –例如循环中加法变为,循环外乘法
19
窥孔优化
• 窥孔 –目标程序中的一个可以移动的窗口 • 优化方法 –冗余存取 –不可到达代码 –控制流优化 –强度削弱 –删除无用操作
cost=6
假定R0, R1和R2中分别存放了a, b和c的地址, 采用: 3. MOV *R1, *R0 ADD *R2, *R0 cost=2 假定R1和R2中分别包含b和c的值, 并且b的值在这 个赋值以后不再需要, 则还可有 4. ADD R2, R1 MOV R1, a cost=3
30
其他考虑
机器指令形式
(op source ,destination) ADD s,d // d+s SUB s,d //d-s MOV s,d //s d
机器指令开销 (cost)
MOV R,M ADD #1 ,R MOV R0,R1 开销 2 开销 2 开销 1
24
• 目标程序
– 绝对机器代码,程序所有的内存地址,特别是程序的起始地址,在编 译时都已经固定。这种代码的优点是装入机器后就可以立即执行,对 于小程序可以快速编译和运行。 – 可重定位机器代码(可重定位目标模块),代码装入内存的起始地址 可以任意改变。一组可重定位的若干目标模块,经过连接和装配后才 可以运行。尽管这些工作增加了程序运行的代价,但是,可重定位机 器代码的优点是灵活性。这种技术允许程序分模块编写,独立地编译 成目标模块,并且从目标模块库中调用其它已经编译好的模块,便于 程序开发。通常,可重定位机器代码中包含可重定位信息和连接信息。 – 如果目标代码是汇编语言程序,还需要汇编后才能运行。只要地址可 以由偏移址及符号表中的其它信息计算得到,代码生成器就可以产生 程序中名字的绝对地址或可重定位地址。这样生成代码的好处是不用 生成二进制的机器代码,而是产生符号指令并用宏机制来帮助产生机 器代码,使得代码生成过程变得容易。 – 为了可读性,采用汇编语言作为目标语言。