lua中复杂表达式的寄存器分配

这个来源通常有三个:upvalue、const、local。除了local变量天然对应寄存器之外,另外的const和upvalue在使用的时候都需要专门的指令来加载到寄存器中,因为大部分的机器操作都是基于寄存器实现。这一点在lua-5.3.4\src\lopcodes.h可以看到。当需要使用一个非寄存器变量时,需要先通过luaK_dischargevars来保证它是“万事俱备,只欠寄存器分配”,在luaK_dischargevars函数中,如果变量是在upvalue中,则需要先生成从up列表中加载该变量的机器指令,但是并没有分配寄存器,也即是它的结果可以放在任意寄存器中,之后该表达式的类型为VRELOCABLE

void luaK_dischargevars (FuncState *fs, expdesc *e) {

……

case VUPVAL: { /* move value to some (pending) register */

e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0);

e->k = VRELOCABLE;

break;

}

……

}

二、复杂表达式的指令生成

以下面表达式为例:

a + b * c

,假设b和c都是upvalue,则首先

对于表达式,当需要使用的时候,在subexpr函数中首先根据算符优先级获得子子表达式,然后调用codebinexpval函数:

static void codebinexpval (FuncState *fs, OpCode op,

expdesc *e1, expdesc *e2, int line) {

int rk2 = luaK_exp2RK(fs, e2); /* both operands are "RK" */

int rk1 = luaK_exp2RK(fs, e1);

freeexps(fs, e1, e2);

e1->u.info = luaK_codeABC(fs, op, 0, rk1, rk2); /* generate opcode */

e1->k = VRELOCABLE; /* all those operations are relocatable */

luaK_fixline(fs, line);

}

对于a+b*c,则codebinexpval函数开始的e1和e2分别表示b和c,所以luaK_exp2RK会为它们分配upvalue的加载和寄存器分配,并且discharge2reg函数会修正之前生成的up加载指令中使用的寄存器。函数codebinexpval之后,b*c作为一个VRELOCABLE类型,重复这个过程。

三、看下代码

tsecer@harry: cat reg.discharge.lua

a = b + c * d

tsecer@harry: ../src/luac -l -l reg.discharge.lua

main <reg.discharge.lua:0,0> (7 instructions at 0x926a70)

0+ params, 3 slots, 1 upvalue, 0 locals, 4 constants, 0 functions

1 [1] GETTABUP 0 0 -2 ; _ENV "b"

2 [1] GETTABUP 1 0 -3 ; _ENV "c"

3 [1] GETTABUP 2 0 -4 ; _ENV "d"

4 [1] MUL 1 1 2

5 [1] ADD 0 0 1

6 [1] SETTABUP 0 -1 0 ; _ENV "a"

7 [1] RETURN 0 1

constants (4) for 0x926a70:

1 "a"

2 "b"

3 "c"

4 "d"

locals (0) for 0x926a70:

upvalues (1) for 0x926a70:

0 _ENV 1 0

tsecer@harry: