读书人

探索Lua5.2内部兑现:编译系统(2) 跳转

发布时间: 2013-01-26 13:47:03 作者: rapoo

探索Lua5.2内部实现:编译系统(2) 跳转的处理

原文

跳转用来控制程序的指令流程。Lua使用OP_JMP指令来执行一个跳转,有关OP_JMP的详细介绍,可以参见《虚拟机指令》。跳转可以分为条件跳转和非条件跳转。非条件跳转比较简单,我们可以先从这里入手。

goto在Lua5.2中,goto和label是新加入的statement,用来执行非条件跳转。这两个statement分别在lparser.c中的gotostat和labelstat函数中被解析。上一篇中讲过,在全局数据Dyndata中,保存着一个goto列表和一个label列表,goto和label使用一个相同的数据结构Labeldesc表示。


回填跳转地址可以分为两类处理:

一种是已经生成的指令位置,Lua会立即将这些跳转指令修改成目标位置。

另一种是当前位置,也就是接下来要生成的指令的位置。在处理当前位置时,Lua并没有立即修改,而是将待修改的跳转指令串接到当前FuncState中的jpc链表上。当使用luaK_code生成下一条指令时,首先会调用dischargejpc函数,将jpc链表上的所有跳转指令修改到这个位置。这等于是延迟回填。之所以这样处理,其实是为了跳转的优化。我们回头看一下luaK_jump函数,它在生成OP_JMP指令时,会将当前jpc串接到新生成的jpc上,并且将jpc清空。这个处理实际的意思是,当生成一个OP_JMP指令时,如果有其他的OP_JMP指令需要跳转到此处,其实就等于间接跳转到新生成的跳转指令的目标位置。所以这些跳转指令不用再跳转到此处,而是直接跳转到新生成的OP_JMP的目标位置,将两步跳转合并成一步。这些指令与新生成跳转指令的目标是一致的,所以可以合并成一个跳转指令集合,等待后面一起回填。

我们接下来在来回顾一下与跳转相关的指令生成api。

luaK_jump用来生成一个新的跳转指令。luaK_concat函数用来将两个链表连接起来合成一个链表。luaK_patchlist用来回填一个链表的跳转位置。luaK_patchtohere用来将一个链表准备回填到当前位置。luaK_patchclose用来回填一个链表中的A,也就是需要关闭的upvalue id,具体可以查看goto的处理。

以上是Lua处理跳转的相关内容。跳转的处理还与逻辑和关系表达式密切相关,我们会在后面表达式部分再进行详细讲解。



读书人网 >编程

热点推荐