name | args | desc |
---|---|---|
OP_MOVE | AB | R(A) := R(B) |
OP_MOVE用來將寄存器B中的值拷貝到寄存器A中。由於Lua是register based vm,大部分的指令都是直接對寄存器進行操作,而不需要對數據進行壓棧和彈棧,所以需要OP_MOVE指令的地方並不多。最直接的使用之處就是將一個local變量複製給另一個local變量時:
- local a;
- local b = a;
- 1 [1] LOADNIL 0 0
- 2 [2] MOVE 1 0
- 3 [2] RETURN 0 1
在編譯過程中,Lua會將每個local變量都分配到一個指定的寄存器中。在運行期,lua使用local變量所對應的寄存器id來操作local變量,而local變量的名字除了提供debug信息外,沒有其他作用。
在這裡a被分配給register 0,b被分配給register 1。第二行的MOVE表示將a(register 0)的值賦給b(register 1)。其他使用的地方基本都是對寄存器的位置有特殊要求的地方,比如函數參數的傳遞等等。
name | args | desc |
---|---|---|
OP_LOADK | A Bx | R(A) := Kst(Bx) |
LOADK將Bx表示的常量表中的常量值裝載到寄存器A中。很多其他指令,比如數學操作指令,其本身可以直接從常量表中索引操作數,所以可以不依賴於LOADK指令。
- local a=1;
- local b="foo";
- 1 [1] LOADK 0 -1 ; 1
- 2 [2] LOADK 1 -2 ; "foo"
- 3 [2] RETURN 0 1
- onstants (2) for 0x80048eb0:
- 1 1
- 2 "foo"
name | args | desc |
OP_LOADKX | A | R(A) := Kst(extra arg) |
LOADKX是lua5.2新加入的指令。當需要生成LOADK指令時,如果需要索引的常量id超出了Bx所能表示的有效範圍,那麼就生成一個LOADKX指令,取代LOADK指令,並且接下來立即生成一個EXTRAARG指令,並用其Ax來存放這個id 。5.2的這個改動使得一個函數可以處理超過262143個常量。
name | args | desc |
OP_LOADBOOL | ABC | R(A) := (Bool)B; if (C) pc++ |
LOADBOOL將B所表示的boolean值裝載到寄存器A中。B使用0和1分別代表false和true。C也表示一個boolean值,如果C為1,就跳過下一個指令。
- local a = true;
- 1 [1] LOADBOOL 0 1 0
- 2 [1] RETURN 0 1
- local a = 1 < 2
對於上面的代碼,一般我們會認為lua應該先對1<2求出一個boolean值,然後放入到a中。然而實際上產生出來的代碼為:
- 1 [1] LT 1 -1 -2 ; 1 2
- 2 [1] JMP 0 1 ; to 4
- 3 [1] LOADBOOL 0 0 1
- 4 [1] LOADBOOL 0 1 0
- 5 [1] RETURN 0 1
- onstants (2) for 0x80048eb0:
- 1 1
- 2 2
可以看到,lua生成了LT和JMP指令,另外再加上兩個LOADBOOL對於a賦予不同的boolean值。LT(後面會詳細講解)指令本身並不產生一個boolean結果值,而是配合後面緊跟的JMP實現true和false的不同跳轉。如果LT評估為true,就繼續執行,也就是執行到JMP,然後調轉到4,對a賦予true;否則就跳過下一條指令到達第三行,對a賦予false,並且跳過下一個指令。所以上面的代碼實際的意思被轉化為:
- local a;
- if 1 < 2 then
- a = true;
- else
- a = false;
- end
邏輯或者關係表達式之所以被設計成這個樣子,主要是為if語句和循環語句所做的優化。不用將整個表達式估值成一個boolean值後再決定跳轉路徑,而是評估過程中就可以直接跳轉,節省了很多指令。
C的作用就是配合這種使用邏輯或關係表達式進行賦值的操作,他節省了後面必須跟的一個JMP指令。
name | args | desc |
OP_LOADNIL | AB | R(A), R(A+1), ..., R(A+B) := nil |
LOADNIL將使用A到B所表示範圍的寄存器賦值成nil。用範圍表示寄存器主要為了對以下情況進行優化:
- local a,b,c;
- 1 [1] LOADNIL 0 2
- 2 [1] RETURN 0 1
對於連續的local變量聲明,使用一條LOADNIL指令就可以完成,而不需要分別進行賦值。
對於一下情況
- local a;
- local b = 0;
- local c;
- 1 [1] LOADNIL 0 0
- 2 [2] LOADK 1 -1 ; 0
- 3 [3] LOADNIL 2 0
- local a,c;
- local b = 0;
No comments:
Post a Comment