第八章 代 码 生 成

65
第第第 第第第第 第第第第第第第第第第 第第第第第第 第第第第第 第第第第第第第第第第第第 ,, 第第第第第 第第 第 第 第 第 第第 第第 第第第 第第 第第 第第 第第 第第 第第

description

前端. 代 码 优 化 器. 代码 生成 器. 中间 代码. 目标 程序. 中间 代码. 源程序. 第八章 代 码 生 成. 本章内容 一个简单的代码生成算法 涉及存储管理,指令选择,寄存器分配和计算次序选择 等基本问题. 8.1 代码生成器的设计中的问题. 8.1.1 目标程序 绝对机器语言程序 目标程序将装入到内存的固定地方 粗略地说,相当于现在的可执行目标模块(第 11 章介绍) 可重定位目标模块 代码中含重定位信息,以适应重定位要求. 8.1 代码生成器的设计中的问题. 8.1.1 目标程序 可重定位目标模块 - PowerPoint PPT Presentation

Transcript of 第八章 代 码 生 成

Page 1: 第八章  代  码  生  成

第八章 代 码 生 成

本章内容• 一个简单的代码生成算法• 涉及存储管理,指令选择,寄存器分配和计

算次序选择等基本问题

前端代 码

优 化 器

中间代码源程序

代码生成器

中间代码

目标程序

Page 2: 第八章  代  码  生  成

8.1 代码生成器的设计中的问题8.1.1 目标程序• 绝对机器语言程序

– 目标程序将装入到内存的固定地方– 粗略地说,相当于现在的可执行目标模块(第 11

章介绍)• 可重定位目标模块

– 代码中含重定位信息,以适应重定位要求

Page 3: 第八章  代  码  生  成

8.1 代码生成器的设计中的问题8.1.1 目标程序• 可重定位目标模块

.L7:testl %eax,%eaxje .L3testl %edx,%edxje .L7movl %edx,%eaxjmp .L7

.L3:leaveret

可重定位目标模块中,需要有绿色部分的重定位信息

Page 4: 第八章  代  码  生  成

8.1 代码生成器的设计中的问题8.1.1 目标程序• 绝对机器语言程序

– 目标程序将装入到内存的固定地方– 粗略地说,相当于现在的可执行目标模块(第 11

章介绍)• 可重定位目标模块

– 代码中含重定位信息,以适应重定位要求– 允许程序模块分别编译– 调用其它先前编译好的程序模块

Page 5: 第八章  代  码  生  成

8.1 代码生成器的设计中的问题8.1.1 目标程序• 绝对机器语言程序• 可重定位目标模块

– 代码中含重定位信息,以适应重定位要求– 允许程序模块分别编译– 调用其它先前编译好的程序模块

• 汇编语言程序– 免去编译器重复汇编器的工作– 从教学角度,增加可读性

Page 6: 第八章  代  码  生  成

8.1 代码生成器的设计中的问题8.1.2 指令的选择• 目标机器指令系统的性质决定了指令选择的

难易程度,指令系统的统一性和完备性是重要的因素

• 指令的速度和机器特点是另一些重要的因素

Page 7: 第八章  代  码  生  成

8.1 代码生成器的设计中的问题• 若不考虑目标程序的效率,指令的选择是直截了

当的

• 例 三地址语句 x = y + z (x , y 和 z 都静态分配 )MOV y, R0 / 把 y 装入寄存器 R0 /

ADDz, R0 / 把 z 加到 R0 上 /

MOV R0, x / 把 R0 存入 x 中 /

逐条语句地产生代码,常常得到低质量的代码

Page 8: 第八章  代  码  生  成

8.1 代码生成器的设计中的问题语句序列 a = b + c

d = a + e的代码如下MOV b, R0ADD c, R0MOV R0, aMOV a, R0ADD e, R0MOV R0, d

Page 9: 第八章  代  码  生  成

8.1 代码生成器的设计中的问题语句序列 a = b + c

d = a + e的代码如下MOV b, R0ADD c, R0MOV R0, aMOV a, R0 -- 多余的指令ADD e, R0MOV R0, d

Page 10: 第八章  代  码  生  成

8.1 代码生成器的设计中的问题语句序列 a = b + c

d = a + e的代码如下MOV b, R0ADD c, R0MOV R0, aMOV a, R0 -- 多余的指令ADD e, R0 -- 若 a 不再使用,MOV R0, d -- 第三条指令也多余

Page 11: 第八章  代  码  生  成

8.1 代码生成器的设计中的问题8.1.3 寄存器分配

运算对象处于寄存器和处于内存相比,指令要短一些,执行也快一些

• 寄存器分配选择驻留在寄存器中的一组变量

• 寄存器指派挑选变量要驻留的具体寄存器

Page 12: 第八章  代  码  生  成

8.1 代码生成器的设计中的问题8.1.4 计算次序的选择

程序中计算的执行次序会影响目标代码的执行效率

• 例 对表达式的计算而言,一种计算次序可能会比其它次序需要较少的寄存器来保存中间结果(见后面例题 3 )

• 选择最佳计算次序是一个 NP 完全问题

Page 13: 第八章  代  码  生  成

8.2 目 标 机 器 8.2.1 目标机器的指令系统• 选择可作为几种微机代表的寄存器机器• 四个字节组成一个字,有 n 个通用寄存器

R0,R1, …,Rn-1

• 二地址指令: op 源,目的MOV { 源传到目的 }

ADD { 源加到目的 }

SUB { 目的减去源 }

Page 14: 第八章  代  码  生  成

8.2 目 标 机 器 • 地址模式和它们的汇编语言形式及附加代价模式 形式 地址 附加代价

绝对地址 M M 1

寄存器 R R 0

变址 c(R) c + contents(R) 1

间接寄存器 R contents(R) 0

间接变址 c(R) contents(c + contents(R)) 1

直接量 #c c 1

Page 15: 第八章  代  码  生  成

8.2 目 标 机 器 • 例 指令实例

MOV R0, M

MOV 4(R0), M4(R0) 的值: contents(4 + contents(R0))

MOV 4(R0), M4(R0) 的值: contents(contents(4 +

contents(R0)))

MOV #1, R0

Page 16: 第八章  代  码  生  成

8.2 目 标 机 器 8.2.2 指令的代价• 指令代价简化为

1 + 指令的源和目的地址模式的附加代价

Page 17: 第八章  代  码  生  成

8.2 目 标 机 器 • 地址模式和它们的汇编语言形式及附加代价模式 形式 地址 附加代价

绝对地址 M M 1

寄存器 R R 0

变址 c(R) c + contents(R) 1

间接寄存器 R contents(R) 0

间接变址 c(R) contents(c + contents(R)) 1

直接量 #c c 1

Page 18: 第八章  代  码  生  成

8.2 目 标 机 器 8.2.2 指令的代价• 指令代价简化为

1 + 指令的源和目的地址模式的附加代价

指令 代价MOV R0 , R1 1

MOV R5 , M 2

ADD #1 , R3 2

SUB 4(R0), 12(R1) 3

Page 19: 第八章  代  码  生  成

8.2 目 标 机 器 • 例 a = b + c , a 、 b 和 c 都静态分配内

存单元- 可生成

MOV b, R0

ADD c, R0 代价 = 6

MOV R0, a

- 也可生成MOV b, a

ADD c, a 代价 = 6

Page 20: 第八章  代  码  生  成

8.2 目 标 机 器 • 例 a = b + c , a 、 b 和 c 都静态分配内存

单元- 若 R0 , R1 和 R2 分别含 a , b 和 c 的地址,则可生成

MOV R1, R0

ADD R2, R0 代价 = 2

- 若 R1 和 R2 分别含 b 和 c 的值,并且 b 的值在这个赋值后不再需要,则可生成

ADD R2, R1

MOV R1, a 代价 = 3

Page 21: 第八章  代  码  生  成

8.3 基本块和流图• 怎样为三地址语句序列生成目标代码?

先给出本节用例prod = 0;i = 1; do { prod = prod + a[i] b[i]; i = i +1;} while (i <= 20);

其 三 地 址 代 码 见 右 边

(1)prod = 0(2) i = 1(3) t1 = 4 i(4) t2= a[t1](5) t3 = 4 i(6) t4 = b[t3](7) t5 = t2 t4

(8) t6 = prod + t5

(9) prod = t6

(10) t7 = i +1(11) i = t7

(12) if i <= 20 goto (3)

Page 22: 第八章  代  码  生  成

8.3 基本块和流图8.3.1 基本块• 基本块

– 连续的语句序列,控制流从它的开始进入,并从它的末尾离开

• 流图– 再用有向边表示基本块

之间的控制流信息,就能得到程序的流图

(1)prod = 0(2) i = 1(3) t1 = 4 i(4) t2= a[t1](5) t3 = 4 i(6) t4 = b[t3](7) t5 = t2 t4

(8) t6 = prod + t5

(9) prod = t6

(10) t7 = i +1(11) i = t7

(12) if i <= 20 goto (3)

Page 23: 第八章  代  码  生  成

8.3 基本块和流图•划分基本块的方法

(1) 首先确定所有入口语句序列的第一个语句是

入口语句能由转移语句转到的

语句是入口语句紧跟在转移语句后面

的语句是入口语句

(1)prod = 0(2) i = 1(3) t1 = 4 i(4) t2= a[t1](5) t3 = 4 i(6) t4 = b[t3](7) t5 = t2 t4

(8) t6 = prod + t5

(9) prod = t6

(10) t7 = i +1(11) i = t7

(12) if i <= 20 goto (3)

Page 24: 第八章  代  码  生  成

8.3 基本块和流图•划分基本块的方法

(2) 每个入口语句到下一个入口语句之前(或到程序结束)的语句序列构成一个基本块

(1)prod = 0(2) i = 1(3) t1 = 4 i(4) t2= a[t1](5) t3 = 4 i(6) t4 = b[t3](7) t5 = t2 t4

(8) t6 = prod + t5

(9) prod = t6

(10) t7 = i +1(11) i = t7

(12) if i <= 20 goto (3)

Page 25: 第八章  代  码  生  成

8.3 基本块和流图(1) prod = 0 (2) i = 1 (3) t1 = 4 i (4) t2= a[t1] (5 ) t3 = 4 i (6 ) t4 = b[t3] (7 ) t5 = t2 t4 (8 ) t6 = prod + t5

(9 ) prod = t6

(10) t7 = i +1(11) i = t7

(12 ) if i <= 20 goto (3)

(1)prod = 0(2) i = 1

(3) t1 = 4 i(4) t2= a[t1](5) t3 = 4 i(6) t4 = b[t3](7) t5 = t2 t4

(8) t6 = prod + t5

(9) prod = t6

(10) t7 = i +1(11) i = t7(12) if i <= 20 goto (3)

B1

B2

Page 26: 第八章  代  码  生  成

8.3 基本块和流图8.3.2 基本块的优化• 术语

– 三地址语句 x = y + z引用 y 和 z 并且对 x 定值– 一个名字的值在基本块的某一点以后还要引用的话,则说这个名字(变量)在该点是活跃的

• 基本块的等价– 两个基本块的出口点有同样的活跃变量集合– 对其中每个变量,定义其值的两个表达式相等

• 有很多等价变换可用于基本块– 局部变换– 全局变换(下一章介绍)

Page 27: 第八章  代  码  生  成

8.3 基本块和流图• 删除局部公共子表达式

a = b + c a = b + cb = a d b = a dc = b + c c = b + cd = a d d = b

• 删除死代码定值 x = y + z 以后不再引用,则称 x 为死变量

可变换成

Page 28: 第八章  代  码  生  成

8.3 基本块和流图• 交换相邻的独立语句

t1 = b + c t2 = x + yt2 = x + y t1 = b + c

当且仅当 t1 和 t2 不相同, x 和 y 都不是 t1 , 并且 b 和 c 都

不是 t2 • 代数变换

x = x + 0 可以删除x = x 1 可以删除x = y 2 改成 x = y y

可变换成

Page 29: 第八章  代  码  生  成

8.3 基本块和流图8.3.3 流图•把控制流信息加到

基本块集合,形成一个有向图来表示程序

• 流图中的节点首结点、前前、后继

prod = 0i = 1

t1 = 4 it2= a[t1]t3 = 4 it4 = b[t3]t5 = t2 t4

t6 = prod + t5

prod = t6

t7 = i +1i = t7

if i <= 20 goto B2

B1

B2

Page 30: 第八章  代  码  生  成

8.3 基本块和流图

•什么是循环 ?– 所有结点是强连通的– 唯一的循环入口

• 外循环和内循环

prod = 0i = 1

t1 = 4 it2= a[t1]t3 = 4 it4 = b[t3]t5 = t2 t4

t6 = prod + t5

prod = t6

t7 = i +1i = t7

if i <= 20 goto B2

B1

B2

Page 31: 第八章  代  码  生  成

8.3 基本块和流图8.3.4 下次引用信息• 为每个三地址语句 x = y op z 决定 x 、 y 和 z

的下次引用信息i: x = y op z

. . . 没有对 x 的赋值j: … = x …

. . . 没有对 x 的赋值k: … = … x

Page 32: 第八章  代  码  生  成

8.3 基本块和流图• 对每个基本块从最后一个语句反向扫描到第

一个语句,可以得到下次引用信息

i: x = y op z . . . 没有对 x 的赋值

j: … = x … . . . 没有对 x 的赋值

k: … = … x•利用下次引用信息,可以压缩临时变量需要

的空间

Page 33: 第八章  代  码  生  成

8.4 一个简单的代码生成器在没有收集全局信息前,暂且以基本块为单位来生成代码

prod = 0i = 1

t1 = 4 it2= a[t1]t3 = 4 it4 = b[t3]t5 = t2 t4

t6 = prod + t5

prod = t6

t7 = i +1i = t7

if i <= 20 goto B2

B1

B2

Page 34: 第八章  代  码  生  成

8.4 一个简单的代码生成器基本考虑:•依次考虑基本块的每个语句,为其产生代码•假定三地址语句的每种算符都有对应的目标

机器算符•假定计算结果留在寄存器中尽可能长的时间 ,除非:– 该寄存器要用于其它计算,或者– 到基本块结束

为此,在生成代码过程中需要记录一些信息

Page 35: 第八章  代  码  生  成

8.4 一个简单的代码生成器8.4.1 寄存器描述和地址描述• 例 对 a = b + c

– 如果寄存器 Ri 含 b , Rj 含 c ,且 b此后不再活跃

产生 ADD Rj, Ri ,结果 a 在 Ri 中– 如果 Ri 含 b ,但 c 在内存单元, b仍然不再活跃

产生 ADD c, Ri ,或者产生MOV c, Rj

ADD Rj, Ri– 若 c 的值以后还要用 ,第二种代码较有吸引力

Page 36: 第八章  代  码  生  成

8.4 一个简单的代码生成器• 在代码生成过程中,需要跟踪寄存器的内容

和名字的地址– 寄存器描述记住每个寄存器当前存的是什么

在任何一点,每个寄存器保存若干个 ( 包括零个 ) 名字的值例

// 语句前, R0 保存变量 a 的值 b = a // 不为该语句产生任何指令

// 语句后, R0 保存变量 a 和 b的值

Page 37: 第八章  代  码  生  成

8.4 一个简单的代码生成器• 在代码生成过程中,需要跟踪寄存器的内容

和名字的地址– 寄存器描述记住每个寄存器当前存的是什么

在任何一点,每个寄存器保存若干个(包括零个)名字的值

– 名字(变量)的地址描述记住运行时每个名字的当前值可以在哪个场所找到

这个场所可以是寄存器、栈单元、内存地址、甚至是它们的某个集合 例 产生 MOV c, R0 后, c 的值可在 R0 和 c 的存储单元找到

Page 38: 第八章  代  码  生  成

8.4 一个简单的代码生成器• 在代码生成过程中,需要跟踪寄存器的内容

和名字的地址– 寄存器描述记住每个寄存器当前存的是什么

在任何一点,每个寄存器保存若干个(包括零个)名字的值

– 名字(变量)的地址描述记住运行时每个名字的当前值可以在哪个场所找到

这个场所可以是寄存器、栈单元、内存地址、甚至是它们的某个集合

– 名字的地址信息存于符号表,另建寄存器描述表– 这两个描述在代码生成过程中是变化的

Page 39: 第八章  代  码  生  成

8.4 一个简单的代码生成器8.4.2 代码生成算法• 对每个三地址语句 x = y op z

– 调用函数 getreg 决定放 y op z 计算结果的场所 L– 查看 y 的地址描述,确定 y 值当前的一个场所 y。如 果 y 的 值还不 在 L 中 , 产 生 指 令 MOV y , L

– 产生指令 op z , L ,其中 z 是 z 的当前场所之一

– 如果 y 和 / 或 z 的当前值不再引用,在块的出口也不活跃,并且还在寄存器中,那么修改寄存器描述

Page 40: 第八章  代  码  生  成

8.4 一个简单的代码生成器8.4.3 寄存器选择函数• 函数 getreg返回保存 x = y op z 的 x 值的场所 L

– 如果名字 y 在 R 中,这个 R 不含其它名字的值 , 并且在执行 x = y op z 后 y 不再有下次引用,那么返回这个 R 作为 L

– 否则,如果有的话,返回一个空闲寄存器– 否则,如果 x 在块中有下次引用,或者 op 是必须

用寄存器的算符,那么找一个已被占用的寄存器R( 可能产生 MOV R , M 指令,并修改 M 的描述 )

– 否则,如果 x 在基本块中不再引用,或者找不到适当的被占用寄存器,选择 x 的内存单元作为 L

Page 41: 第八章  代  码  生  成

8.4 一个简单的代码生成器• 赋值语句 d = (a b) + (a c) + (a c)

编译产生三地址语句序列:t1 = a b

t2 = a c

t3 = t1 + t2

d = t3 + t2

Page 42: 第八章  代  码  生  成

8.4 一个简单的代码生成器语 句 生成的代码 寄存器描述 名字的地址描述

寄存器空 t1 = a b MOV a, R0

SUB b, R0 R0 含 t1 t1 在 R0 中

t2 = a c MOV a, R1SUB c, R1

R0 含 t1

R1 含 t2

t1 在 R0 中t2 在 R1 中

t3 = t1+ t2 ADD R1,R0 R0 含 t3

R1 含 t2

t3 在 R0 中t2 在 R1 中

d = t3 + t2 ADD R1,R0 R0 含 d d 在 R0 中 MOV R0, d d 在 R0 和 内 存

Page 43: 第八章  代  码  生  成

8.4 一个简单的代码生成器语 句 生成的代码 寄存器描述 名字的地址描述

寄存器空 t1 = a b MOV a, R0

SUB b, R0 R0 含 t1 t1 在 R0 中

t2 = a c MOV a, R1SUB c, R1

R0 含 t1

R1 含 t2

t1 在 R0 中t2 在 R1 中

t3 = t1+ t2 ADD R1,R0 R0 含 t3

R1 含 t2

t3 在 R0 中t2 在 R1 中

d = t3 + t2 ADD R1,R0 R0 含 d d 在 R0 中 MOV R0, d d 在 R0 和 内 存

Page 44: 第八章  代  码  生  成

8.4 一个简单的代码生成器语 句 生成的代码 寄存器描述 名字的地址描述

寄存器空 t1 = a b MOV a, R0

SUB b, R0 R0 含 t1 t1 在 R0 中

t2 = a c MOV a, R1SUB c, R1

R0 含 t1

R1 含 t2

t1 在 R0 中t2 在 R1 中

t3 = t1+ t2 ADD R1,R0 R0 含 t3

R1 含 t2

t3 在 R0 中t2 在 R1 中

d = t3 + t2 ADD R1,R0 R0 含 d d 在 R0 中 MOV R0, d d 在 R0 和 内 存

Page 45: 第八章  代  码  生  成

8.4 一个简单的代码生成器语 句 生成的代码 寄存器描述 名字的地址描述

寄存器空 t1 = a b MOV a, R0

SUB b, R0 R0 含 t1 t1 在 R0 中

t2 = a c MOV a, R1SUB c, R1

R0 含 t1

R1 含 t2

t1 在 R0 中t2 在 R1 中

t3 = t1+ t2 ADD R1,R0 R0 含 t3

R1 含 t2

t3 在 R0 中t2 在 R1 中

d = t3 + t2 ADD R1,R0 R0 含 d d 在 R0 中 MOV R0, d d 在 R0 和 内 存

Page 46: 第八章  代  码  生  成

8.4 一个简单的代码生成器语 句 生成的代码 寄存器描述 名字的地址描述

寄存器空 t1 = a b MOV a, R0

SUB b, R0 R0 含 t1 t1 在 R0 中

t2 = a c MOV a, R1SUB c, R1

R0 含 t1

R1 含 t2

t1 在 R0 中t2 在 R1 中

t3 = t1+ t2 ADD R1,R0 R0 含 t3

R1 含 t2

t3 在 R0 中t2 在 R1 中

d = t3 + t2 ADD R1,R0 R0 含 d d 在 R0 中 MOV R0, d d 在 R0 和 内 存

Page 47: 第八章  代  码  生  成

8.4 一个简单的代码生成器语 句 生成的代码 寄存器描述 名字的地址描述

寄存器空 t1 = a b MOV a, R0

SUB b, R0 R0 含 t1 t1 在 R0 中

t2 = a c MOV a, R1SUB c, R1

R0 含 t1

R1 含 t2

t1 在 R0 中t2 在 R1 中

t3 = t1+ t2 ADD R1,R0 R0 含 t3

R1 含 t2

t3 在 R0 中t2 在 R1 中

d = t3 + t2 ADD R1,R0 R0 含 d d 在 R0 中 MOV R0, d d 在 R0 和 内 存

Page 48: 第八章  代  码  生  成

8.4 一个简单的代码生成器• 前三条指令可以修改,使执行代价降低

修改前 修改后MOV a, R0 MOV a, R0

SUB b, R0 MOV R0,R1

MOV a, R1 SUB b, R0

SUB c, R1 SUB c, R1

. . . . . .

Page 49: 第八章  代  码  生  成

8.4 一个简单的代码生成器8.4.4 为变址和指针语句产生代码

• 变址与指针运算的三地址语句的处理和二元算符的处理相同

Page 50: 第八章  代  码  生  成

8.4 一个简单的代码生成器8.4.5 条件语句

• 实现条件转移有两种方式– 根据寄存器的值是否为下面六个条件之一进行分支:负、零、正、非负、非零和非正

– 用条件码来表示计算的结果或装入寄存器的值是负、零还是正

Page 51: 第八章  代  码  生  成

8.4 一个简单的代码生成器1、根据寄存器的值是否为下面六个条件之一

进行分支:负、零、正、非负、非零和非正

• 例 if x < y goto z

– 把 x 减 y 的值存入寄存器 R– 如果 R 的值为负,则跳到 z

Page 52: 第八章  代  码  生  成

8.4 一个简单的代码生成器2、用条件码的例子• 例 若 if x < y goto z | 则: x = y + w

的实现是: | if x < 0 goto z

CMP x, y | 的实现是:CJ< z | MOV y , R0

| ADD w , R0

| MOV R0 , x

| CJ< z

Page 53: 第八章  代  码  生  成

本 章 要 点• 代码生成器设计中的主要问题:存储管理、

计算次序的选择、寄存器的分配、指令的选择等

• 目标机器几种常用的地址模式和一些常用的指令

• 基本块和程序流图• 简单的代码生成算法

Page 54: 第八章  代  码  生  成

例 题 1

在 SPARC/SUNOS 上,经某编译器编译,程序的结果是 120。把第 4 行的 abs(1)改成 1 的话,则程序结果是

1

int fact(){

static int i=5;

if(i==0) { return(1); }

else { i=i-1; return((i+abs(1)) fact());}

}

main(){

printf("factor of 5 = %d\n", fact());

}

先完成有函数调用的子表达式的计算

Page 55: 第八章  代  码  生  成

例 题 2

下面的程序在 X86/Linux 机器上编译后的运行结果是 7 ,而在 SPARC/SUNOS 机器上编译后的运行结果是 6。试分析运行结果不同的原因。main() {

long i;

i = 0;printf("%ld\n", (++i)+(++i)+(++i) );

} / ++i 就是 i = i + 1 /

Page 56: 第八章  代  码  生  成

例 题 2

按一般的代码生成, ++i 的计算结果保留在寄存器中,因此这三个 ++i 的计算次序不会影响最终的结果。结果应该是 6

++

= ==

i

i +

1 i

i +

1 i

i +

1

Page 57: 第八章  代  码  生  成

例 题 2

按一般的代码生成, ++i 的计算结果保留在寄存器中,因此这三个 ++i 的计算次序不会影响最终的结果。结果应该是 6

结果是 7 的话,一定是某个 ++i 的结果未保留在寄存器中。上层计算对它的引用落在另一个 ++i 的计算的后面

++

= ==

i

i +

1 i

i +

1 i

i +

1

Page 58: 第八章  代  码  生  成

例 题 2

• 如果机器有 INC 指令的话,编译器极可能产生一条 INC 指令来完成 ++i

• X86/Linux 机器上果真是这么做的 +

+

= ==

i

i +

1 i

i +

1 i

i +

1

Page 59: 第八章  代  码  生  成

例 题 2

将表达式改成 (++i)+((++i)+(++i)), 结果会怎样?

• 在 SPARC/SUNOS 机器上的结果仍然是 6• 在 X86/Linux 机器上的结果是 9

+

+

== =

i

i +

1 i

i +

1 i

i +

1

Page 60: 第八章  代  码  生  成

例 题 3

下面 C 语言程序如下 , 运行时输出 105, 为什么?

main() {long i;

 i=10;i=(i+5) + (i=i5);printf("%d\n", i);

}寄存器分配策略可能导致先计算右子表达式

二元运算

表达式 表达式 需 2 个 R 需 3 个 R

需几个 R

Page 61: 第八章  代  码  生  成

例 题 4

下面是一个 C 语言程序,在 X86/Linux 机器上编译(未使用优化)该程序得到的汇编代码见下页(为便于理解,略去了和讨论本问题无关的部分,并改动了一个地方)

main() { long i,j;

if ( j ) i++; else while ( i ) j++;}

Page 62: 第八章  代  码  生  成

例 题 4main() { incl -4(%ebp)

long i,j; jmp .L3 long i,j; .L2: .L4: (写在一行以省空

间 ) if ( j ) cmpl $0,-4(%ebp) i++; jne .L6 else jmp .L5 while ( i ) j++; .L6:} incl -8(%ebp) pushl %ebp jmp .L4 movl %esp,%ebp .L5: .L3: .L1: subl $8,%esp leave cmpl $0,-8(%ebp) ret je .L2

Page 63: 第八章  代  码  生  成

例 题 4

• 为什么会出现一条指令前有多个标号的情况,如 .L2和 .L4 ,还有 .L5 、 .L3 和 .L1 ?从控制流语句的中间代码结构加以解释

• 条件语句和循环语句的中间代码结构如下:if (E) then S1 else S2 while (E) do S

E 的代码 L4: E 的代码假转 L2 真转 L6

S1 的代码 转 L5转 L3 L6: S 的代码

L2: S2 的代码 转 L4L3: L5:

• 当 while 语句作为条件语句的 S2时,会出现所说情况

Page 64: 第八章  代  码  生  成

例 题 4

• 每个函数都有这样的标号 .L1 ,它的作用是什么,为什么本函数没有引用该标号的地方?

. . .

jmp .L4 .L5: .L3: .L1:

leave ret

• .L1 标号定义的入口是返回调用者时该执行的指令,在函数内部有 return 语句时就会跳转到 .L1

Page 65: 第八章  代  码  生  成

习 题• 第一次: 8.4 (只为 8.1(e) 生成代码)