第九章 目标代码生成
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
对基本块中的中间代码序列,按怎样的次 序来生成目标代码? 改写为如下中间代码 序列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
接上页: 置初值; 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;
例 考察基本块 (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.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 )。
计算变量待用信息的算法:
(1)开始时,符号表中各变量为“非待用”,变量在出口 之后是否活跃填入活跃信息栏; (2)从基本块出口到入口由后向前依次处理各个中间代 码。对每一条代码 i: A:=B op C,执行步骤:
把符号表中变量A的待用和活跃信息附加到中间代码i上; 把符号表中A的待用和活跃信息置为“非待用”、“非活 跃”; 把符号表中变量B.C的待用和活跃信息附加到中间代码i上; 把符号表中B.C的待用信息置为i,活跃信息置为“活跃”。
各中间代码对应的目标代码:
序号 中间代码 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
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的目标代码
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)
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
类型 直接地址型 寄存器型 变址型 间址型
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则转移
第 9章
目标代码生成
以源程序的中间代码作为输入,产生等价 的目标代码作为输出,如图。
编译前端 中间 代码 代码优化 中间 代码生成器 代码 目标程序
源程序
符号表
代码生成器的输入包括中间代码和符号表中
的信息; 代码生成是把语义分析后或优化后的中间代 码变换成目标代码。
9.1
概述
1. 代码生成器的输入: 源程序的中间表示:如三地址代码; 符号表中的信息:在数据区中的相对地址 2. 目标程序: 绝对机器语言代码; 可再定位机器语言代码; 汇编语言程序。
acde f:=a-d cdef
B2
acdf B3 e:=a-c
B4
bdef
分配好寄存器后,就可以生成目标代码。
与简单代码生成器不同之处在于: (1)固定分配了寄存器的变量用相应reg表示; (2)循环前置结点中存值到寄存器; (3)循环出口结点中存值到主存单元; (4)循环中每个基本块的出口,未固定分配 reg的变量按需要回存到主存单元,固定分 配了reg的变量不须回存到主存单元。
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
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
优 化
为了能够进行上述优化,代码生成器必须了解 一些信息:
9.3.1
待用信息与活跃信息
变量在基本块内的待用信息:从基本块的 出口由后向前扫描,对每个变量建立相应 的待用信息链和活跃变量信息链。 简化:基本块中的所有临时变量均看作基本 块出口之后的非活跃变量;所有非临时变 量均看作基本块出口之后的活跃变量。
接上页:
按上述算法给出的结点次序, T[1],T[2],…,T[N],可以把DAG重新 生成一个等价的中间代码序列,根据 新序列中的中间代码次序,运用前面 的代码生成算法生成的目标代码较优。
9.4
代码生成器的自动生成技术
机器体系结构不统一 自动生成的代码的质量、生成速度 与机器无关的优化
把变量a的值调入寄存器时,应替换哪个寄存 器的内容才能计算更快?
对于循环,把可用的几个寄存器固定分配给节省 执行代价最多的那几个变量。 节省代价计算公式: [USE(M,B)+ 2*LIVE(M,B)]
BL
其中,USE(M,B)=基本块B中对M定值前引用M的次数; LIVE(M,B)=1(当M在B中被定值且在B的出口之后是活跃的) 0(其他情况)
把目标机的每条指令的形式描述作为输入 将中间语言代码与这种描述进行匹配来产生相 应的指令。
问题:
采用由形式描述驱动的技术:
小结:
在基本块内,利用DAG重排中间代码生成目 标代码的次序,以得到较少的目标代码; 充分利用寄存器,使目标代码的执行更快; 选择合适的指令,使生成的指令序列更短 更高效。
3 4
5
A=B A=B[I]
A[I]=B
注:处理完所有代码后,对现行值只在reg中,而在基本块的出口 后是活跃的变量,要用ST指令把值存放到主存单元中。
9.3.3
寄存器分配
有效利用寄存器,生成更高效的目标代码。 指令的执行代价:指令访问主存单元次数+1 如: op Ri,Rj 执行代价为1 op Ri,M 执行代价为2 op Ri,*Rj 执行代价为2 op Ri,*M 执行代价为3
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
结论:
对表达式X=A*B+C*D的求值,从右往左算得到的 目标代码较优。 利用基本块的DAG,将基本块的中间代码序列 重新排列。 给DAG中的N个内部结点重新排序的算法:
例 考查如下中间代码 序列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
接上页: 置初值; 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;
例 考察基本块 (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.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 )。
计算变量待用信息的算法:
(1)开始时,符号表中各变量为“非待用”,变量在出口 之后是否活跃填入活跃信息栏; (2)从基本块出口到入口由后向前依次处理各个中间代 码。对每一条代码 i: A:=B op C,执行步骤:
把符号表中变量A的待用和活跃信息附加到中间代码i上; 把符号表中A的待用和活跃信息置为“非待用”、“非活 跃”; 把符号表中变量B.C的待用和活跃信息附加到中间代码i上; 把符号表中B.C的待用信息置为i,活跃信息置为“活跃”。
各中间代码对应的目标代码:
序号 中间代码 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
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的目标代码
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)
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
类型 直接地址型 寄存器型 变址型 间址型
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则转移
第 9章
目标代码生成
以源程序的中间代码作为输入,产生等价 的目标代码作为输出,如图。
编译前端 中间 代码 代码优化 中间 代码生成器 代码 目标程序
源程序
符号表
代码生成器的输入包括中间代码和符号表中
的信息; 代码生成是把语义分析后或优化后的中间代 码变换成目标代码。
9.1
概述
1. 代码生成器的输入: 源程序的中间表示:如三地址代码; 符号表中的信息:在数据区中的相对地址 2. 目标程序: 绝对机器语言代码; 可再定位机器语言代码; 汇编语言程序。
acde f:=a-d cdef
B2
acdf B3 e:=a-c
B4
bdef
分配好寄存器后,就可以生成目标代码。
与简单代码生成器不同之处在于: (1)固定分配了寄存器的变量用相应reg表示; (2)循环前置结点中存值到寄存器; (3)循环出口结点中存值到主存单元; (4)循环中每个基本块的出口,未固定分配 reg的变量按需要回存到主存单元,固定分 配了reg的变量不须回存到主存单元。
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
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
优 化
为了能够进行上述优化,代码生成器必须了解 一些信息:
9.3.1
待用信息与活跃信息
变量在基本块内的待用信息:从基本块的 出口由后向前扫描,对每个变量建立相应 的待用信息链和活跃变量信息链。 简化:基本块中的所有临时变量均看作基本 块出口之后的非活跃变量;所有非临时变 量均看作基本块出口之后的活跃变量。
接上页:
按上述算法给出的结点次序, T[1],T[2],…,T[N],可以把DAG重新 生成一个等价的中间代码序列,根据 新序列中的中间代码次序,运用前面 的代码生成算法生成的目标代码较优。
9.4
代码生成器的自动生成技术
机器体系结构不统一 自动生成的代码的质量、生成速度 与机器无关的优化
把变量a的值调入寄存器时,应替换哪个寄存 器的内容才能计算更快?
对于循环,把可用的几个寄存器固定分配给节省 执行代价最多的那几个变量。 节省代价计算公式: [USE(M,B)+ 2*LIVE(M,B)]
BL
其中,USE(M,B)=基本块B中对M定值前引用M的次数; LIVE(M,B)=1(当M在B中被定值且在B的出口之后是活跃的) 0(其他情况)
把目标机的每条指令的形式描述作为输入 将中间语言代码与这种描述进行匹配来产生相 应的指令。
问题:
采用由形式描述驱动的技术:
小结:
在基本块内,利用DAG重排中间代码生成目 标代码的次序,以得到较少的目标代码; 充分利用寄存器,使目标代码的执行更快; 选择合适的指令,使生成的指令序列更短 更高效。
3 4
5
A=B A=B[I]
A[I]=B
注:处理完所有代码后,对现行值只在reg中,而在基本块的出口 后是活跃的变量,要用ST指令把值存放到主存单元中。
9.3.3
寄存器分配
有效利用寄存器,生成更高效的目标代码。 指令的执行代价:指令访问主存单元次数+1 如: op Ri,Rj 执行代价为1 op Ri,M 执行代价为2 op Ri,*Rj 执行代价为2 op Ri,*M 执行代价为3
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
结论:
对表达式X=A*B+C*D的求值,从右往左算得到的 目标代码较优。 利用基本块的DAG,将基本块的中间代码序列 重新排列。 给DAG中的N个内部结点重新排序的算法: