# 探索Lua5.2內部實現:虛擬機指令(7) 關系和邏輯指令
| name | args | desc |
|---|---|---|
| OP_JMP | A sBx | pc+=sBx; if (A) close all upvalues >= R(A) + 1 |
JMP執行一個跳轉,sBx表示跳轉的偏移位置,被加到當前指向下一指令的指令指針上。如果sBx為0,表示沒有任何跳轉;1表示跳過下一個指令;-1表示重新執行當前指令。如果A>0,表示需要關閉所有從寄存器A+1開始的所有local變量。實際執行的關閉操作只對upvalue有效。
JMP最直接的使用就是對應lua5.2新加入的goto語句:
~~~
::l::??
goto?l;??
~~~
~~~
1???????[1]?????JMP?????????????0?-1????;?to?1??
2???????[2]?????RETURN??????????0?1??
~~~
這是一個無限循環。第一行JMP的sBx為-1,表示重新執行JMP。
~~~
do??
????local?a;??
????function?f()?a?=?1?end??
end??
~~~
~~~
main <test.lua:0,0> (5 instructions at 0x80048eb0)
0+ params, 2 slots, 1 upvalue, 1 local, 1 constant, 1 function
1 [2] LOADNIL 0 0
2 [3] CLOSURE 1 0 ; 0x80049128
3 [3] SETTABUP 0 -1 1 ; _ENV "f"
4 [3] JMP 1 0 ; to 5
5 [4] RETURN 0 1
constants (1) for 0x80048eb0:
1 "f"
locals (1) for 0x80048eb0:
0 a 2 5
upvalues (1) for 0x80048eb0:
0 _ENV 1 0
function <test.lua:3,3> (3 instructions at 0x80049128)
0 params, 2 slots, 1 upvalue, 0 locals, 1 constant, 0 functions
1 [3] LOADK 0 -1 ; 1
2 [3] SETUPVAL 0 0 ; a
3 [3] RETURN 0 1
constants (1) for 0x80049128:
1 1
locals (0) for 0x80049128:
upvalues (1) for 0x80049128:
0 a 1 0
~~~
上面的代碼在do block中創建了一個局部變量a,并且a作為upvalue在函數f中被引用到。到退出do block是,a會退出他的有效域,并且關閉他對應的upvalue。Lua5.2中去除了以前專門處理關閉upvalue的指令CLOSE,而把這個功能加入到了JMP中。所以,生成的指令第四行的JMP在這里沒有執行跳轉,而只是為了關閉a的upvalue。
JMP其他的功能就是配合邏輯和關系指令(統稱為test指令),實現程序的條件跳轉。每個test輯指令與JMP搭配,都會將接下來生成的指令分為兩個集合,滿足條件的為true集合,否則為false集合。當test條件滿足時,指令指針回+1,跳過后面緊跟的JMP指令,然后繼續執行。當test條件不滿足時,則繼續執行,也就到了JMP,然后跳轉到分支代碼。

| name | args | desc |
|---|---|---|
| OP_EQ | A B C | if ((RK(B) == RK(C)) ~= A) then pc++ |
| OP_LT | A B C | if ((RK(B) < ?RK(C)) ~= A) then pc++ |
| OP_LE | A B C | if ((RK(B) <= RK(C)) ~= A) then pc++ |
關系指令對RK(B)和RK(C)進行比較,然后將比較結果與A指定的boolean值進行比較,來決定最終的boolean值。A在這里為每個關系指令提供了兩種比較目標,滿足和不滿足。比如OP_LT何以用來實現“”。
~~~
local?a,b,c;??
a?=?b?
~~~
~~~
1???????[1]?????LOADNIL?????????0?2??
2???????[2]?????LT??????????????1?1?2??
3???????[2]?????JMP?????????????0?1?????;?to?5??
4???????[2]?????LOADBOOL????????0?0?1??
5???????[2]?????LOADBOOL????????0?1?0??
6???????[2]?????RETURN??????????0?1??
~~~
第二行的LT對寄存器1和2進行LT比較,如果結果為true,則繼續執行后面的JMP,跳轉到第五行的LOADBOOL,將寄存器0賦值為true;如果結果為false,則跳過后面的JMP,執行第四行的LOADBOOL,將寄存器0賦值為false。我們前面講過關于LOADBOOL,第四行執行后會跳過第五行的賦值。
| name | args | desc |
|---|---|---|
| OP_TEST | A C | if not (R(A) C) then pc++ |
| OP_TESTSET | A B C | if (R(B) C) then R(A) := R(B) else pc++ |
邏輯指令用于實現and和or邏輯運算符,或者在條件語句中判斷一個寄存器。TESTSET將寄存器B轉化成一個boolean值,然后與C進行比較。如果不相等,跳過后面的JMP指令。否則將寄存器B的值賦給寄存器A,然后繼續執行。TEST是TESTSET的簡化版,不需要賦值操作。
~~~
local?a,b,c;??
a?=?b?and?c;??
~~~
~~~
1???????[1]?????LOADNIL?????????0?2??
2???????[2]?????TESTSET?????????0?1?0??
3???????[2]?????JMP?????????????0?1?????;?to?5??
4???????[2]?????MOVE????????????0?2??
5???????[2]?????RETURN??????????0?1??
~~~
第二行的TESTSET將寄存器1的值與false比較。如果不成立,跳過JMP,執行第四行的MOVE,將寄存器2的值賦給寄存器0。否則,將寄存器1的值賦給寄存器0;然后執行后面的JMP。
上面的代碼等價于
~~~
if?b?then??
?a?=?c??
else??
?a?=?b??
end??
~~~
- 前言
- 探索Lua5.2內部實現:TString
- 探索Lua5.2內部實現:虛擬機指令(1) 概述
- 探索Lua5.2內部實現:虛擬機指令(2) MOVE & LOAD
- 探索Lua5.2內部實現:虛擬機指令(3) Upvalues & Globals
- 探索Lua5.2內部實現:虛擬機指令(4) Table
- 探索Lua5.2內部實現:虛擬機指令(7) 關系和邏輯指令
- 探索Lua5.2內部實現:虛擬機指令(8) LOOP
- 探索Lua5.2內部實現:編譯系統(1) 概述
- 探索Lua5.2內部實現:編譯系統(2) 跳轉的處理
- 探索Lua5.2內部實現:編譯系統(3) 表達式
- 探索Lua5.2內部實現:編譯系統(4) 表達式分類
- 探索Lua5.2內部實現:Garbage Collection(1) 原理
- 探索Lua5.2內部實現:Garbage Collection(2)