C编译器剖析_6.3.3 汇编代码生成_为跳转指令产生汇编代码
2015-04-30 17:08
267 查看
6.3.3 为跳转指令产生汇编代码
在这一小节中,我们要为“有条件跳转”、“无条件跳转”和“间接跳转”产生相应的汇编指令。中间指令的四元式如下所示:
<运算符opcode,目的操作数DST,源操作数SRC1,源操作数SRC2>
(1) 有条件跳转,例如“if (a <= b) goto BB2;”,四元式为:
<JLE,BB2,a,b>
////////对应的汇编代码//////////
movl a, %eax //把SRC1的值暂存在寄存器eax中
cmpl b, %eax //比较eax和b的大小
jle .BB2 //进行有条件跳转
(2) 无条件跳转,例如“goto BB3;”,四元式为:
<JMP,BB3,NULL,NULL>
////////////对应的汇编代码///////////////
jmp .BB3
(3) 间接跳转,例如“goto (BB4,BB5,BB6)[t0];”,在翻译switch语句时会产生间接跳转指令,其四元式为:
<IJMP,[BB4,BB5,BB6,NULL],t0,NULL>
////////////对应的汇编代码///////////////
.data
swtchTable1: .long .BB4
.long .BB5
.long .BB6
.text
jmp*swtchTable1(,%eax,4)
由于跳转指令位于基本块的最末尾,我们在第6.2节的“图6.2.3 EmitBBlock()”中,已对x87栈顶寄存器做过回写操作。在本小节,我们还要中调用ClearRegs函数,对于x86 CPU中的寄存器进行回写,如图6.3.7第7行、第26行和第54行所示。控制流即将随着跳转指令的执行而进入另一个基本块,而UCC编译器仅在同一基本块内对公共子表达式进行重用,因此,即使跳转指令里的操作数DST、SRC1和SRC2是临时变量,我们没有必要为其“长期”分配寄存器,换言之,我们不会调用在前面的章节中介绍过的AllocateReg函数,而是调用如图6.3.7第13行所示的PutInReg函数,把操作数SRC1的值暂存到某个寄存器中。
图6.3.7第1至30行的EmitBranch函数用于为“有条件跳转”产生汇编代码,当操作数是浮点数时,我们在第8行调用EmitX87Branch函数来处理。当操作数是整数时,我们会在第11行做进一步判断。由于常数会以“立即数”的形式存在于代码区中,当程序运行时,CPU会从代码区里预读机器指令,从而把立即数也加载入CPU,因此当操作数SRC2是常数时,我们可以不必把SRC1的值加载到寄存器中,这不会违反“同一条X86汇编指令的两个操作数不可以都在内存中”的寻址要求。这意味着我们可以生成形如“cmpl
$3, a”的比较指令,但不可以生成形如“cmpl b, a”的比较指令。图6.3.7第11至14行会在“SRC2存在且SRC2不是常数时”,通过第13行调用的PutInReg函数,把SRC1的值加载到某个寄存器中。第17行判断SRC1的值是否在寄存器中,如果已载入寄存器,我们可在第18行把中间指令里的源操作数SRC1改为SRC1->reg,之后在就可生成形如“cmpl b, %eax”的比较指令。第28行调用PutASMCode函数来产生比较和跳转指令,形如“cmpl b, %eax;jle .BB2
”。
![](http://img.blog.csdn.net/20150430170936888?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvU2hlSXND/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图6.3.7 EmitBranch()和EmitJump()
图6.3.7第31至39行的EmitX87Branch用于处理浮点数的有条件跳转,我们会在第34行先把操作数SRC1加载到x87栈顶寄存器中,然后在第37行生成浮点数比较和跳转的指令,形如第35至36行的模板所示。我们已在“1.5 结合C语言来学汇编”介绍过这些指令,这里不再重复。图6.3.7第50至56行的函数EmitJump会为无条件跳转生成汇编代码。
接下来,我们来讨论一下为“间接跳转”产生汇编代码的函数EmitIndirectJump,如图6.3.8所示。图6.3.8第7行调用PutInReg函数把操作数SRC1加载到寄存器中,第10行用于产生“.data”,表示接下来的内容为数据区,第11至17行创建一个名称形如“swtchTable1”的符号对象,第23至33行会在数据区中创建跳转表,形如第19至21行所示。第36行输出“.text”,表示接下来的内容为代码区。由于已在第7行把SRC1加载到寄存器reg中,我们就可在汇编指令中使用该寄存器的名称,为此我们要在第37行把中间指令的SRC1改为相应的寄存器。第38行调用ClearRegs对x86
CPU中的寄存器进行了必要的回写,第41行调用PutASMCode产生了形如“jmp*swtchTable1(,%eax,4)”的汇编指令。
![](http://img.blog.csdn.net/20150430171109753?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvU2hlSXND/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图6.3.8 EmitIndirectJump()
在这一小节中,我们要为“有条件跳转”、“无条件跳转”和“间接跳转”产生相应的汇编指令。中间指令的四元式如下所示:
<运算符opcode,目的操作数DST,源操作数SRC1,源操作数SRC2>
(1) 有条件跳转,例如“if (a <= b) goto BB2;”,四元式为:
<JLE,BB2,a,b>
////////对应的汇编代码//////////
movl a, %eax //把SRC1的值暂存在寄存器eax中
cmpl b, %eax //比较eax和b的大小
jle .BB2 //进行有条件跳转
(2) 无条件跳转,例如“goto BB3;”,四元式为:
<JMP,BB3,NULL,NULL>
////////////对应的汇编代码///////////////
jmp .BB3
(3) 间接跳转,例如“goto (BB4,BB5,BB6)[t0];”,在翻译switch语句时会产生间接跳转指令,其四元式为:
<IJMP,[BB4,BB5,BB6,NULL],t0,NULL>
////////////对应的汇编代码///////////////
.data
swtchTable1: .long .BB4
.long .BB5
.long .BB6
.text
jmp*swtchTable1(,%eax,4)
由于跳转指令位于基本块的最末尾,我们在第6.2节的“图6.2.3 EmitBBlock()”中,已对x87栈顶寄存器做过回写操作。在本小节,我们还要中调用ClearRegs函数,对于x86 CPU中的寄存器进行回写,如图6.3.7第7行、第26行和第54行所示。控制流即将随着跳转指令的执行而进入另一个基本块,而UCC编译器仅在同一基本块内对公共子表达式进行重用,因此,即使跳转指令里的操作数DST、SRC1和SRC2是临时变量,我们没有必要为其“长期”分配寄存器,换言之,我们不会调用在前面的章节中介绍过的AllocateReg函数,而是调用如图6.3.7第13行所示的PutInReg函数,把操作数SRC1的值暂存到某个寄存器中。
图6.3.7第1至30行的EmitBranch函数用于为“有条件跳转”产生汇编代码,当操作数是浮点数时,我们在第8行调用EmitX87Branch函数来处理。当操作数是整数时,我们会在第11行做进一步判断。由于常数会以“立即数”的形式存在于代码区中,当程序运行时,CPU会从代码区里预读机器指令,从而把立即数也加载入CPU,因此当操作数SRC2是常数时,我们可以不必把SRC1的值加载到寄存器中,这不会违反“同一条X86汇编指令的两个操作数不可以都在内存中”的寻址要求。这意味着我们可以生成形如“cmpl
$3, a”的比较指令,但不可以生成形如“cmpl b, a”的比较指令。图6.3.7第11至14行会在“SRC2存在且SRC2不是常数时”,通过第13行调用的PutInReg函数,把SRC1的值加载到某个寄存器中。第17行判断SRC1的值是否在寄存器中,如果已载入寄存器,我们可在第18行把中间指令里的源操作数SRC1改为SRC1->reg,之后在就可生成形如“cmpl b, %eax”的比较指令。第28行调用PutASMCode函数来产生比较和跳转指令,形如“cmpl b, %eax;jle .BB2
”。
图6.3.7 EmitBranch()和EmitJump()
图6.3.7第31至39行的EmitX87Branch用于处理浮点数的有条件跳转,我们会在第34行先把操作数SRC1加载到x87栈顶寄存器中,然后在第37行生成浮点数比较和跳转的指令,形如第35至36行的模板所示。我们已在“1.5 结合C语言来学汇编”介绍过这些指令,这里不再重复。图6.3.7第50至56行的函数EmitJump会为无条件跳转生成汇编代码。
接下来,我们来讨论一下为“间接跳转”产生汇编代码的函数EmitIndirectJump,如图6.3.8所示。图6.3.8第7行调用PutInReg函数把操作数SRC1加载到寄存器中,第10行用于产生“.data”,表示接下来的内容为数据区,第11至17行创建一个名称形如“swtchTable1”的符号对象,第23至33行会在数据区中创建跳转表,形如第19至21行所示。第36行输出“.text”,表示接下来的内容为代码区。由于已在第7行把SRC1加载到寄存器reg中,我们就可在汇编指令中使用该寄存器的名称,为此我们要在第37行把中间指令的SRC1改为相应的寄存器。第38行调用ClearRegs对x86
CPU中的寄存器进行了必要的回写,第41行调用PutASMCode产生了形如“jmp*swtchTable1(,%eax,4)”的汇编指令。
图6.3.8 EmitIndirectJump()
相关文章推荐
- C编译器剖析_6.3.1 汇编代码生成_由中间指令产生汇编代码的主要流程
- C编译器剖析_6.3.4 汇编代码生成_为函数调用与返回产生汇编代码
- C编译器剖析_6.3.2 汇编代码生成_为算术运算产生汇编代码
- C编译器剖析_6.3.6 汇编代码生成_为“取地址”产生汇编指令
- C编译器剖析_6.1 汇编代码生成_简介
- C编译器剖析_6.3.5 汇编代码生成_为类型转换产生汇编代码
- C编译器剖析_5.4.1 中间代码生成与优化_删除无用的临时变量和优化跳转目标
- C编译器剖析_6.2 汇编代码生成_寄存器的管理
- C编译器剖析_5.4.2 中间代码生成及优化_基本块的合并
- C编译器剖析_5.3.2 中间代码生成及优化_switch语句的翻译
- C编译器剖析_5.3.1 中间代码生成及优化_If语句和复合语句的翻译
- C编译器剖析_5.2.6 中间代码生成及优化_一元表达式及其他表达式的翻译
- C编译器剖析_5.2.2 中间代码生成及优化_再论符号symbol与公共子表达式
- C编译器剖析_5.2.3 中间代码生成及优化_通过“偏移”访问数组元素和结构体成员
- C编译器剖析_5.2.5 中间代码生成及优化_赋值表达式的翻译
- C编译器剖析_5.1 中间代码生成及优化_简介
- 编译并连接从helloworld.c生成的汇编代码的方法步骤
- arm第八天(汇编指令之跳转指令)
- 使用hsdis查看jit生成的汇编代码