C编译器剖析_5.2.1 中间代码生成及优化_布尔表达式的翻译

本文详细介绍了C编译器在处理布尔表达式和算术表达式时的中间代码生成与优化。针对布尔表达式,如a&&b,编译器采用短路运算,仅在必要时计算表达式。而对于算术表达式,如a+b,编译器会先计算整个表达式的值并保存到临时变量中。文章通过具体例子解释了如何生成条件跳转指令,并讨论了TranslateExpression和TranslateBranch函数在处理不同类型的表达式时的角色。最后,文章探讨了短路运算符如&&和||在翻译时的处理方式,以及如何生成跳转指令。
摘要由CSDN通过智能技术生成

5.2.1  中间代码生成与优化_布尔表达式的翻译

    我们仍然按照语法分析和语义检查时的思路,先讨论表达式的翻译,再处理语句。表达式从概念上来说,可分为算术表达式和布尔表达式,在一些编程语言(例如Java)中对这两者是有严格区分的,算术表达式的结果是整数或浮点数,而布尔表达式的结果是逻辑上的真或假。布尔是英国数学家,由于布尔较早进行了关于“与或非”逻辑运算的研究,为了纪念这位先驱,在Java中引入了关键字boolean,而在C++中引入了bool关键字来表达逻辑上的真或假。C语言中并没有专门的布尔类型,而是把非0的值当作真,把0当作假。

int a, b;

if(a+b){// Java中非法,a+b为算术值,非布尔值;C中合法

}

if(a == b){//a == b为布尔值,在C或Java中都合法

}

    为了讨论的方便,我们不妨把C语言看成是存在布尔表达式,例如a&&b,a||b或者a>b等,也存在算术表达式,例如a+b或者a&b,但是不存在C程序员可见的布尔类型关键字bool或者boolean。对于算术表达式而言,C编译器需要对整个表达式进行求值,例如,我们要计算出“a和b相加的值”,或者“a和b按位与的值”,并把运算结果保存到一个临时变量中,这个临时变量的值就代表了整个表达式的值。而对于布尔表达式来说,我们并不会对整个表达式进行求值,而是生成相应的跳转指令,对于a&&b而言,当a为假时,不论b为何值,a&&b的值都为假,所以此时我们并不需要对b进行求值。若把“a&&b”换成“f()&&g()”,则当f()的返回值为假时,我们不需要对函数g()进行求值,此时g()函数根本就没有被调用,这就是C语言中的短路运算。

     我们先举个简单的例子来说明这些概念,如图5.2.1所示,第1至21行给出了一个简单的C程序,第22至60行是与之对应的中间代码。对于第4至8行的C代码来说,与其对应的中间代码在第24至31行,我们需要先对第4行的算术表达式a+b进行求值,第25行的中间代码“t0:a+b”表示把“a+b”求值后的结果存于临时变量t0中,对表达式进行求值的工作由ucl\tranexpr.c中的TranslateExpression()函数来完成。但对于第9行的布尔表达式a&&b来说,我们只是生成第33行和第35行的条件跳转指令,并没有对整个布尔表达式a&&b进行求值,但a为假时,控制流直接由第33行转移到基本块BB6处,即不再需要判断b的值。产生这些跳转指令的工作由ucl\tranexpr.c中的TranslateBranch()函数来实现。在X86汇编中,跳转指令对应的助记符为Jump,缩写为J;但在Arm汇编中,跳转指令对应的是Branch,缩写为B,单词Branch是分支的意思(也有些人写为分枝,哪个是错别字,whoknows!),这意味着如果是“有条件跳转”的话,控制流就会“开叉”,相当于一根树枝分成若干个细枝了,例如图5.2.1第35行的有条件跳转,控制流可能流向第37行,也可能流向第40行。


图5.2.1 布尔表达式和算术表达式

    对于图5.2.1第14行的算术表达式a&b来说,我们需要先对整个表达式a&b进行求值,再根据其结果进行跳转,如图第42和43行所示。比较特殊的是第19行的布尔表达式a||b,按我们之前的介绍,翻译布尔表达式时,我们只是产生一些跳转指令来改变控制流,如第50行和第52行所示。但按照第19行赋值语句的语义,我们需要在a||b为真时,给变量c赋值1;在a||b为假时,给变量赋值0。因此,C编译器需要产生第54至57行的代码。这些工作由ucl\tranexpr.c中的函数TranslateBranchExpression来完成。

    简而言之,如果我们需要显式地求出表达式的值,就调用函数TranslateExpression();如果只是需要生成一些跳转指令来改变当前的控制流,则使用TranslateBranch()。如果我们在调用TranslateExpression()来对表达式求值时,遇到的表达式是形如图5.2.1第19行的赋值语句中的布尔表达式a||b,此时我们就再调用TranslateBranchExpression()函数来处理,进而产生如图5.2.1第50至57行的代码。

    趁热打铁,我们现在就来看一下函数TranslateBranchExpression的代码,如图5.2.2第1至18行所示。第4行通过调用CreateTemp函数创建了一个临时变量,不妨设这个临时变量为t。我们需要根据布尔表达式的控制流来执行t=0或者t=1的代码,第11行调用GenerateMove函数产生t = 0的代码,第15行则用于产生t = 1。当表达式expr为假时,我们在执行完t=0后,还要通过第12行的GenerateJump函数,产生一条无条件跳转指令,跳过t=1这个指令。中间代码t=0是基本块falseBB的第一条指令,而t=1为基本块trueBB的第一条指令。在调用第8行的TranslateBranch函数来产生跳转指令时,我们需要把trueBB和falseBB作为跳转目标传递给TranslateBranch函数,这样我们才能生成形如如图5.2.1第50至52行的跳转指令。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值