码迷,mamicode.com
首页 > 其他好文 > 详细

C编译器剖析_5.3.1 中间代码生成及优化_If语句和复合语句的翻译

时间:2015-04-20 16:58:25      阅读:142      评论:0      收藏:0      [点我收藏+]

标签:c编译器   复合语句的翻译   if语句的翻译   ucc   

5.3.1   If语句和复合语句的翻译

    我们先简单回顾一下对布尔表达式的翻译,我们通过调用TranslateBranch函数来产生跳转指令,从而实现布尔表达式的语义。在使用函数TranslateBranch(expr, bt, bn)时,有这么两个约定:

    (1) 当expr为真时,跳往bt基本块;

    (2) 紧随“函数TranslateBranch所生成的跳转指令”之后的基本块为bn。

   在表达式的基础上,我们来讨论一下语句的翻译。图5.3.1用于if语句的翻译,第4至8行说明了如何翻译“if(expr) stmt”,我们需要创建trueBB和nextBB这两个基本块;而第12至18行阐述了如何翻译“if(expr)  stmt  else stmt”语句,我们需要创建trueBB、falseBB和nextBB这3个基本块。图5.3.1第29至34行的代码用于翻译“if(expr) stmt”,而第35至43行的代码用于翻译“if(expr) stmt else stmt”。

技术分享

图5.3.1 TranslateIfStatement()

    由图5.3.1可以发现,在理解了表达式翻译的前提下,if语句的翻译就较容易理解了。而其他的控制流语句,包括do语句、while语句和for语句也与此类似,我们就不再啰嗦。接下来,我们来讨论复合语句的翻译。

    语法上,复合语句的产生式如下所示,由可选的声明列表和语句列表构成。其中的声明可以带有初值。

CompoundStatement  -->  { DeclarationListopt  StatementListopt}

    由于局部变量的存储空间是运行时在栈中动态分配的,UCC编译器需要在编译时产生中间代码来实现对局部变量的初始化,而在对应的汇编代码中,由于无法预知相应的存储单元,我们只能采用“ebp寄存器+常量偏移”的模式来对其寻址。程序运行时,我们会在栈中为被调用的函数分配一块内存,用于存放其形参、局部变量、函数返回地址等信息,这块内存通常被称为“活动记录activation  record”或者“帧frame”。大部分的C编译器,会使用x86的ebp寄存器来指向当前函数的活动记录;而“常量偏移”则可由C编译器在编译时,根据局部变量声明在函数中出现的先后顺序和所占内存大小来确定。

    对于以下局部数组arr的初始化,我们可以先把arr所占的栈空间清0,然后再根据“初值及其偏移”来产生初始化相应数组元素的指令。

int  arr[8] = {10,20,30};

    我们在语义检查时介绍过以下结构体struct  initData,其中的offset即可用于描述偏移,而expr对应的表达式为初值,而next用于构造链表。

         struct  initData{

         int offset;                                  //偏移

         AstExpression expr;               //初值对应的表达式

         InitData next;

};

typedef  struct  initData * InitData;

     上述初值{10,20,30}经语义检查后,我们可得到以下链表,在中间代码生成阶段,我们

可由该链表产生“对相应数组元素进行初始化”的中间代码。

(表达式10,偏移0)  --> (表达式20,偏移4) --> (表达式30,偏移8)

///////对应的中间代码////////////

arr : 40;             //把arr所占40字节栈内存清0

arr[0] = 10;                         

arr[4] = 20;

arr[8] = 30;

   而到了汇编层次,局部变量的名字不再可用,我们改用“ebp寄存器+常量偏移”的方

式来表示局部变量。对应的汇编代码如下所示:

         pushl$40

         pushl$0

         leal-40(%ebp), %eax

         pushl%eax

         callmemset              //对数组arr清0,相当于调用memset(&arr[0], 0, 40);

         addl$12, %esp

         movl$10, -40(%ebp)        //arr[0] = 10;

         movl$20, -36(%ebp)

         movl$30, -32(%ebp)

    对于以下局部数组num来说,由于初值{1,2,3,4}覆盖了数组num所占的所有内存空间,我们并不需要额外产生对数组num进行清0的指令。

         int num[4] = {1,2,3,4};

    有了这些基础后,我们就来看一下复合语句的翻译,如图5.3.2所示,第1至9行的函

数HasPaddingSpace用来判断初值之间是否有空隙,例如上述arr数组的初值间就有空隙,而数组num之间则没有。这可通过累加各初值所占内存大小,然后再与整个局部对象所占内存大小作比较,即可得到。

技术分享

图5.3.2 TranslateCompoundStatement()

     图5.3.2第17至43行的for循环用来处理各个局部变量,第26行调用GenerateClear

函数,产生“对整个局部对象清0”的指令。第30行判断初值表达式的类型,当表达式为字符串时,我们在第32行调用AddString得到一个代表该字符串的符号;对于其他表达式,则在第36行递归地调用TranslateExpression来翻译。第38行调用CreateOffset函数生成一个形如前文“arr[4]”的符号,第40行通过调用GenerateMove函数产生形如“arr[4] =20;”的中间代码。图5.3.2第44至47行的while循环递归地调用TranslateStatement来实现对语句的翻译。

    在下一小节中,我们来讨论一下switch语句的翻译,transtmt.c中的不少代码与此相关,

由于使用了跳转表的技术,UCC编译器为switch产生的代码还是相当高效的。

C编译器剖析_5.3.1 中间代码生成及优化_If语句和复合语句的翻译

标签:c编译器   复合语句的翻译   if语句的翻译   ucc   

原文地址:http://blog.csdn.net/sheisc/article/details/45151191

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!