您的位置:首页 > 编程语言

C编译器剖析_5.2.5 中间代码生成及优化_赋值表达式的翻译

2015-04-18 17:53 435 查看
5.2.5 赋值表达式的翻译
在这一小节中,我们来讨论一下赋值表达式的翻译,例如“a=a-b;”或者“a += b;”等。在图5.2.13的下方我们给出了表达式“a+=b;”在语义检查前后的语法树,右下侧的语法树相当于是表达式a = a’+b,但表达式中的a和a’对应同一个语法树结点。按C的语义,语句“a+=b;”中的a只能被求值一次,而不能求值两次。例如,若a结点对应一个函数调用(*f()),则语句“(*f())
+= b;”中的函数f只被调用一次。在图5.2.13的上方是C语句“a = a +b;”的语法树,在该树中有两个语法树结点a,若把a改为表达式(*f()),则我们需要为语句“(*f()) =(*f()) + b;”产生两次对函数f的调用。



图5.2.13 赋值表达式的语法树
对于赋值语句“a = b;”来说,当a是结构体位域成员时,对a的写操作会较复杂。下面我们还是举一个例子来说明。以下结构体对象dt中的位域b1、b2、b3和b4共占用32位内存(即4字节),其所处偏移为4,在中间代码层次,我们可用符号“dt[4]”来表示该内存单元。

struct Data{

int a; //偏移为0

//以下各成员构成的位模式为“b4_b3_b2_b1”,

//其中,低8位为b1,高4位为b4

intb1:8; //偏移为4,pos为0

intb2:16; //偏移为4,pos为8

intb3:4; //偏移为4,pos为24

intb4:4; //偏移为4,pos为28

int d; //偏移为8

} dt;
struct Data * ptr = &dt;
ptr->d = 30;
dt.b2 = val;

当C程序员要通过“dt.b2 = val;”来对位域b2进行赋值时,我们可按以下步骤来实现:
(1)按以下中间代码来构造位模式“b4_b3_val_b1”

t1: val << 8; // LSH指令,左移8位

t2: t1 & 0x00FFFF00; //BAND指令,得到位模式“0...0_0...0_val_0...0”

t3:dt[4] & 0xFF0000FF; //BAND指令,得到位模式“b4_b3_0…0_b1”

t4: t2 | t3; //BOR指令,得到位模式“b4_b3_val_b1”

(2)把位模式“b4_b3_val_b1”赋值给dt[4],即可实现对b2的赋值,同时保持b1、b3和b4不会发生变化。

dt[4] = t4;

当我们面对“ptr->d = 30;”时,UCC编译器为ptr->d产生的中间代码如下所示,由于t7是个临时变量,如果对t7进行赋值,则ptr->num的值并没有发生变化。我们要对t6所指向的内存单元进行赋值,才能改变ptr->num的值。

t5: ptr
t6: t5 + 8; //成员d的偏移为0
t7: *t6;
此时我们可产生形如“(IMOV, t6,30);”的中间代码,其中IMOV表示把30赋值给t6所指向的内存单元,而不是把30赋值给t6。UCC编译器的GenerateIndirectMove函数用于产生IMOV指令。若t6的值存放在寄存器eax中,则最终生成的汇编代码可以是“movl $30, (%eax)”,对应的中间代码可表示为:
*t6 = 30; //而非 t6 = 30;
有了这些基础之后,我们就可以来分析一下赋值表达式的翻译,如图5.2.14所示。第29至60行的WriteBitFiled函数用于实现对位域成员“dt.b2”的赋值操作,按前文的介绍,对于形如“dt.b2= val;”表达式来说,我们要构造位模式“b4_b3_val_b1”来对符号“dt[4]”进行赋值。图5.2.14第46至50行分别用于产生前文中我们介绍过的“左移LSH”、“按位与BAND”、“按位与BAND”和“按位或BOR”这4条指令,从而得到位模式“b4_b3_val_b1”。当C程序员编写的是形如“ptr->b2=val;”的语句,我们需要在第54行产生一条IMOV指令(形如“*t
= val;”)来实现赋值,这需要进行间接寻址;而当C程序员编写的是形如“dt.b2 = val;”的语句时,我们在第57行产生MOV指令(形如“dt[4] = val;”)即可,不必进行间接寻址。对赋值表达式“dt.b2 = val”来说,虽然按C的语义,整个表达式“dt.b2 = val”不可充当左值来使用(即“(dt.b2= val) = 3;”是非法的),但赋值操作后,dt.b2的值即为整个表达式的值。因此,我们在第59行调用函数ReadBitField来读取dt.b2的值,用来作为整个表达式“dt.b2
= val”的值。当“dt.b2 = val;”中的val为常数时,我们可在编译时进行一些常量折叠的处理,避免生成一些不必要的指令,函数WriteBitFiled中的其他代码主要用于这些情况,结合图中的注释不难理解相关代码,我们就不再啰嗦。



图5.2.14 TranslateAssignmentExpression()
当我们面对的是形如“a+=b;”的赋值表达式时,UCC编译器在图5.2.14第5行调用函数TranslateExpression,递归地对与a对应的左子树进行翻译。由图5.2.13右下方的语法树,为了避免对a对应的子树进行重复翻译,我们在第8行把左子树a的根结点当作一个标识符结点OP_ID,这样即使当a为(*f())时,我们也只会产生一次函数调用f。另一个需要特殊处理的是当a为“dt.b2”这样的位域成员时,若我们面对的是形如“dt.b2
+= b;”这样的赋值表达式,则图5.2.14第5行真正调用的函数是我们在上一节图5.2.11中介绍的函数TranslateMemberAccess。当位域成员dt.b2处于赋值号的左侧时,在TranslateMemberAccess函数中,我们并未调用ReadBitFiled进行读操作,因此在图5.2.14第10行我们还要对此进行判断。对于“dt.b2 += b;”而言,当我们第一次访问到dt.b2的子树时,在图5.2.14第5行的函数调用返回后,dst对应的符号为“dt[4]”。由于dt.b2是位域,我们需要在第11行调用ReadBitFiled函数进行读操作,这将产生以下中间代码,其中符号“t2”就保存了dt.b2的值,我们需要在第9行把符号“t2”保存在dt.b2对应子树的根结点,当我们第二次访问到dt.b2对应的子树时,其根结点已被当成一个标识符结点OP_ID,其符号名为“t2”,这样我们就可在稍后产生形如“t3:
t2+b”的加法指令。
t1 : dt[4] << 8;
t2 : t1 >> 16;

图5.2.14第13行递归地调用TranslateExpression来翻译赋值运算符的右子树,第15行调用WriteBitFiled来实现对位域成员的写操作,第16至22行用于处理形如“ptr->d=30;”的赋值运算,我们需要在第21行产生形如“*t = 30;”的IMOV指令用于间接寻址,而第25行产生形如“a = b;”的MOV指令。
在此基础上,我们再来理解“a++”或“++a”这样的运算就轻松了许多,图5.2.15给出了这两个表达式在语义检查后的语法树。若a在执行自加运算前的值为val,按照C语言的语义,经过a++和++a的运算后,a的值都变为(val+1),但表达式“a++”的值为val,而表达式“++a”的值为(val+1)。需要注意的是,在C语言中,“a++”和“++a”都不可以作为左值来使用,即“(a++)
=3;”和“(++a) = 5;”都是非法的;但在C++语言中,“++a”可充当左值使用,即“(++a) = 5;”是合法的,但“(a++) =3;”是非法的。这也是C语言不是C++语言子集的一个例证。



图5.2.15 a++和++a的语法树
结合图5.2.15,我们可以来理解一下对自加运算进行翻译的函数TranslateIncrement,如图5.2.16所示。按照我们在图5.2.14对“a+=b”的翻译,对于“++a”来说,我们递归地调用TranslateIncrement(expr->kids[0])即可完成翻译,这可通过图5.2.16第5行和第22行来实现。但对于“a++”来说,我们要先计算出图5.2.15结点a的值,该值即为整个表达式“a++”的值,之后再进行“a+=1”的运算来使a的值加1。图5.2.16第6行递归地调用TranslateExpression函数完成了对a对应子树的翻译,第7行把该子树置为标识符结点OP_ID,也是为了避免对a子树的多次求值。对于“a++”或“a--”而言,当结点a为位域成员时,我们需要通过第14行的ReadBitField函数来读取该位域的值;在a结点不是形如“t1:*t2”的临时变量时,我们在第16和17行创建一个临时变量来保存表达式“a++”的值;否则,我们面对的是形如“ptr->d”的非位域成员,此时第13至18行的if条件都不成立,我们在第12行即已保存“t1:
*t2;”中的符号t1。接下来,在第19行真正调用的函数是TranslateAssignmentExpression,用于实现对结点a的加1运算,第20行返回“a++”整个表达式的值。



图5.2.16 TranslateIncrement()
在下一节中,我们来分析一下tranexpr.c中的其他函数,例如一元运算表达式的翻译。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: