6.3.6 为“取地址”产生汇编指令
在这一小节中,我们来讨论一下以下两条中间指令的翻译:
(1)取地址指令<ADDR,DST,SRC1,NULL>
例如 <ADDR,t0, number, NULL>,表示取number的地址并保存到临时变量t0中
(2)对象清零指令< CLR,DST,SRC1,NULL>
例如<CLR,arr,16,NULL>,表示把arr所占16字节的内存清零
我们先举一个例子来说明,对于图6.3.14第4行局部数组arr的初始化来说,我们可以先通过第11行的CLR中间指令把数组arr所占16字节内存清零,然后再通过第12行的MOV指令对arr[0]进行赋值。第21至26行是CLR指令对应的汇编代码,我们调用在汇编中调用库函数memset实现了对象清零的操作。而对于第5行的“ptr1 = &num1;”而言,我们可以通过第13行的ADDR中间指令来取num1的地址,并保存于临时变量t0中,再通过第14行的MOV指令对ptr1进行赋值。第28行“leal
num1,%eax”是ADDR指令对应的汇编代码。
图6.3.14 取地址和对象清零的例子
与取地址指令有关的中间指令还有第17行的DEREF指令和第18行的IMOV指令,我们已在前面的章节中介绍过这两条中间指令的翻译。通过这两条中间指令,我们可以实现第7行的C语句“*ptr1 = *ptr2;”所需的语义。
接下来,我们来看看用于产生相应汇编代码的函数EmitAddress和函数EmitClear,如图6.3.15所示。第5行调用AllocateReg函数为ADDR指令里的临时变量DST分配一个寄存器,第7行产生汇编指令把SRC1的地址加载到DST对应的寄存器中,第8行调用ModifyVar来设置临时变量DST的回写标志位,表示“其在内存中的值”与“在寄存器中的值”已经不一致。
6.3.15 EmitAddress()和EmitClear()
图6.3.15第10至35行用于为CLR指令产生汇编代码,当要清零的对象大小为{1,2,4}字节时,我们可以在第15至22行产生mov汇编指令来实现清零,否则我们就通过第32行产生汇编代码,在其中调用memset函数来实现对象清零。由于在汇编指令模板X86_CLEAR中使用了寄存器eax,我们需要在第28行调用SpillReg函数对寄存器eax进行回写。
至此,我们完成了为各中间指令产生汇编代码的工作。
最后,让我们再看一下Compile函数,如图6.3.16所示,这是我们从第3章开始一路走来的路线图,我们历经了“语法分析”、“语义检查”、“中间代码生成及优化”和“汇编代码生成”这几个阶段。
图6.3.16 Compile()