# 探索Lua5.2內部實現:虛擬機指令(2) MOVE & LOAD
| name | args | desc |
|---|---|---|
| OP_MOVE | A B | 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 | A B C | 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???
~~~
C在這里的作用比較特殊。要了解C的具體用處,首先要知道lua中對于邏輯和關系表達式是如何處理的,比如:
` local?a?=?1?`
對于上面的代碼,一般我們會認為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?
????a?=?true;??
else??
????a?=?false;??
end??
~~~
邏輯或者關系表達式之所以被設計成這個樣子,主要是為if語句和循環語句所做的優化。不用將整個表達式估值成一個boolean值后再決定跳轉路徑,而是評估過程中就可以直接跳轉,節省了很多指令。
C的作用就是配合這種使用邏輯或關系表達式進行賦值的操作,他節省了后面必須跟的一個JMP指令。
| name | args | desc |
|---|---|---|
| OP_LOADNIL | A B | 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???
~~~
在Lua5.2中,a和c不能被合并成一個LOADNIL指令。所以以上寫法理論上會生成更多的指令,應該予以避免,而改寫成
~~~
local?a,c;??
local?b?=?0; ?
~~~
- 前言
- 探索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)