lua源码分析3(条件跳转)

  rel="File-List" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml"> rel="themeData" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx"> rel="colorSchemeMapping" href="file:///C:%5CDOCUME%7E1%5CADMINI%7E1%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml">

2 关于条件编译

         首先,解释一下,为什么题目叫做:条件编译。其实很简单,现在这一章要分析的是,ifwhilerepeatfor语句。这些语句有个什么特点呢?那就是,都要有条件判断。根据条件判断的结果,以决定是否执行,该如何执行。我不知道该怎么称呼这样的语句,就一致称其为:条件编译。因为这一章的重点是研究,条件对于中间码的生成造成的影响,以及如何生成中间码以实现这种条件判断。

         首先分析if then elseif then else then end句型。

         这个很简单,实际上是lua处理的很简单。

         lua遇到了if这个关键字的时候,就开始调用ifstat(),首先,这是进入一个块。这个块这个东西和函数是相同的重要,因为它关系到一个变量的作用域的问题。块由block()函数处理。不过在此之前,要注意一个重要的事情,那就是条件判断语句。lua是怎么处理条件判断的呢?

         首先,lua会跳过if关键字,然后,会进入一个十分常用的函数:cond(),这个函数还是比较复杂的,我现在要再看一遍这个函数,还是心有余悸的。

         cond()这个函数比较短,我还是贴出来吧:

         static int cond (LexState *ls) {

        /* cond -> exp */

        expdesc v;

        expr(ls, &v);  /* read condition */

       if (v.k == VNIL) v.k = VFALSE;  /* `falses' are all equal here */

       luaK_goiftrue(ls->fs, &v);

        return v.f;

    }

         第一步,用expr()这个函数读取条件表达式,条件表达式是一个式子,可以是以下几种情况:

1、  常数:这种情况比较好办,如果是FALSENIL就是条件不通过,其他情况都是条件通过;

2、  另一种情况是非常数:也就是说,不是编译期就能判断是否为TRUEFALSE,要到运行期来判断。

 

         对于这两种情况,分别分析,当然,首先是简单的情况,常数。

         当遇到常数的时候,expr()比较happy。因为它处理起来比较简单,也就是通过simpleexp()直接处理。比如,这个常数是个数字100simpleexp()就会把这个表达式v赋值为:v->k = TK_NUMBER,将v->u.nval = ls->t.seminfo.r,也就是把100 v->u.nval。如果这个常数是一个字符串,simpleexp()会将这个字符串注册进常量表,并将 v->k = TK_STRINGv->u.s.info赋值为这个字符串在常量表中的位置。如果这个常量是FALSE,NIL,TRUE3个保留字,simpleexp()就会直接将v->k赋值成 TK_FALSETK_NILTK_TRUE

         然后,lua会调用luaK_goiftrue()函数。这个函数就是生成判断指令的函数了。

         首先,这个函数会把要用到的变量discharge到空闲寄存器上去。也就是生成对应的加载变量的指令。由于现在我们研究的是常量,就不需要这一步,直接跳过。

         然后,lua会判断这个常量是FALSE呢还是TRUE呢。如果是TRUE,则将pc = NO_JUMP,这个pc就是说,如果要生成指令的话,就会用这个pc指向这个指令。如果是FASLE,则会生成一条无条件跳转指令:通过luaK_jump()。这条指令会跳到哪呢?下面会有两种情况:

1、 后面是elseif,这时,就要跳到这条elseif前面,来做下一次条件判断;

2、 后面是else,这时,就跳到这条else前面或后面,因为else本身不生成指令。

         怎么实现跳转到这个地方呢?当解析程序解析到if语句开始时,如果要跳转,就要跳过这个if块,到达下一个elseifelse程序块。但是,解析程序怎么知道下一个elseifelse块的开头地址在哪呢?当然,刚开始解析程序是不知道的。只有解析程序把这个if块解析完了,才能知道原来要跳转到这里。所以,之前要把要跳转的那条指令的位置记录下来,到解析完了if块后,再将那条指令跳转的位置修正到此处。lua中就是这么实现的。

         具体就是,lua会通过luaK_pathtohere()函数,将要跳转的那条指令保存在fs->jpc里,这个fs->jpc就是指上次要跳转的指令。但是,lua不会立即将跳转指令修改到这里。因为这个elseifelse块中可能没有代码,也就是说,就算跳到这里也是没有的,不会执行任何代码,这时,lua就进行了优化,只有当lua在这个块里生成代码的时候,也就是在luaK_code()函数里,调用dischargejpc(),将保存在fs->jpc里的指令修改为跳到这个地方。

         基本的跳转原理就是这样的 了。明白了这些后,再来看复杂条件的跳转是怎么实现的。也就是,在编译期间并不知道条件的结果,到底是真还是假,也就是说,要到运行期间才知道到底要不要跳转。假设我们现在研究这个例子:

         例:if a > b then end

         这里,就要回到cond()函数里了。看cond()函数是怎么处理a > b这种运行时才能出结果的情况。

         首先,cond()会通过调用simpleexp()读取a,然后会读取下一个操作符,发现是>,这个时候,还不能生成代码,因为这里关系到一个操作符优先级的问题,要接着读取后面的操作符,先把优先级高的操作符处理后,才能处理低优先级的操作符,所以,这里就会进行递归读取。在此之前,还有一个问题,那就是,以逻辑操作符连接的式子,比如:ANDOR等,这些操作符的话,其实前面就是一个式子,比如AND,如果是 a AND b,如果a错的话,后面的就不用测试了,如果对的话,还要继续测试b,所以,在这里,要为 a生成一个测试及跳转的语句,这里就用到一个函数jumponcond()

         这个函数是怎么实现的呢?这个函数其实是通过调用condjump()实现的。具体来说,很简单,condjump()生成了2个指令,第一条指令是判断指令:OP_TEST或者OP_TESTSET,第二条指令是OP_JUMP,也就是说,如果测试的结果是FALSE,那么,OP_TEST不做任何事情,继续执行,那么就会执行到OP_JUMP指令了,也就是FALSE的时候不执行if块,直接跳转,如果测试成功了呢,OP_TEST会执行跳过下一条指令的操作,也就是跳过了OP_JUMP,继续执行if块。

         这就是条件跳转的基本实现原理。

 

         现在回到simpleexp()中来,继续我们的例子:a > b。当读取了>好,会先用luaK_infix()函数进行逻辑ANDOR这两个逻辑运算的代码生成。在这个例子中,没有逻辑运算,于是,lua会继续。这个时候,像前面所说的,要进行操作符优先级的运算,于是,进行递归simpleexp()这个函数,在递归的时候,发现后面只有一个b,那么,就会读取b ,然后,就进入关键的地方了,luaK_posfix()函数。这个函数会根据操作符的不同,生成操作指令,比如我们现在的>,就会通过codecomp()函数,生成OP_LT指令。这个OP_LT指令的执行其实进行了优化,也就是说,如果测试结果为FALSE的话,也就是不通过,理论上是要进入下一条指令跳转的,在执行期间,这两条指令会在一个指令周期里执行,如果测试不通过,直接从下一条跳转指令中取出要跳转的位置,然后跳转到那里。如果测试成功的话,就会跳过下一天条跳转指令,进入if块。

         这就是条件跳转代码编译的过程。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值