探索Lua 5.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コマンドの場所は多くありません.最も直接的な使用点は、1つのlocal変数を別のlocal変数にコピーする場合です.
コンパイル中、Luaは各local変数を指定されたレジスタに割り当てます.ランタイムではluaはlocal変数に対応するレジスタidを使用してlocal変数を操作するが、local変数の名前はdebug情報を提供する以外に役に立たない.
ここで、aはregister 0に割り当てられ、bはregister 1に割り当てられる.2行目のMOVEは、a(register 0)の値をb(register 1)に付与することを示す.他の使用箇所は,関数パラメータの伝達など,レジスタの位置に特別な要求がある箇所が基本である.
name
args
desc
OP_LOADK
A Bx
R(A) := Kst(Bx)
LOADKは、Bxで表される定数テーブルの定数値をレジスタAにロードする.数学操作命令のような他の多くの命令は、それ自体が定数テーブルからオペランドを直接インデックスすることができるので、LOADK命令に依存しなくてもよい.
name
args
desc
OP_LOADKX
A
R(A) := Kst(extra arg)
LOADKXはlua 5である.2新しく追加されたコマンド.LOADK命令を生成する必要がある場合、インデックスの定数idがBxで表すことができる有効範囲を超えている場合、LOADK命令の代わりにLOADKX命令を生成し、次に直ちにEXTRAARG命令を生成し、そのAxでこのidを格納する.5.2のこの変更により、1つの関数が262143個を超える定数を処理することができる.
name
args
desc
OP_LOADBOOL
A B C
R(A) := (Bool)B; if (C) pc++
LOADBOOLは、Bで表されるboolean値をレジスタAにロードする.Bはfalseとtrueをそれぞれ0と1で表す.Cはboolean値も表し,Cが1であれば次の命令をスキップする.
上記のコードについては,luaはまず1<2に対してboolean値を求め,aに入れるべきであると考えられる.しかし、実際に生成されたコードは次のとおりです.
luaはLTとJMP命令を生成し,さらに2つのLOADBOOLを加えてaに対して異なるboolean値を与えることが分かる.LT(後述)命令自体はboolean結果値を生成するのではなく,後続のJMPに合わせてtrueとfalseの異なるジャンプを実現する.LTがtrueと評価された場合、実行は継続され、すなわちJMPに実行され、その後4に移行し、aにtrueが付与される.そうでなければ、次の命令をスキップして3行目に達し、aにfalseを付与し、次の命令をスキップする.上記のコードの実際の意味は次のように変換されます.
論理式または関係式がこのように設計されたのは,主に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変数宣言では、個別に値を割り当てる必要がなく、1つのLOADNIL命令を使用して完了できます.
状況について
name
args
desc
OP_MOVE
A B
R(A) := R(B)
OP_MOVEは、レジスタBの値をレジスタAにコピーするために用いられる.Luaはregister based vmであるため、ほとんどの命令はレジスタを直接操作し、データをスタックやスタックを弾く必要がないのでOP_が必要であるMOVEコマンドの場所は多くありません.最も直接的な使用点は、1つの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に割り当てられる.2行目の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
constants (2) for 0x80048eb0:
1 1
2 "foo"
name
args
desc
OP_LOADKX
A
R(A) := Kst(extra arg)
LOADKXはlua 5である.2新しく追加されたコマンド.LOADK命令を生成する必要がある場合、インデックスの定数idがBxで表すことができる有効範囲を超えている場合、LOADK命令の代わりにLOADKX命令を生成し、次に直ちにEXTRAARG命令を生成し、そのAxでこのidを格納する.5.2のこの変更により、1つの関数が262143個を超える定数を処理することができる.
name
args
desc
OP_LOADBOOL
A B C
R(A) := (Bool)B; if (C) pc++
LOADBOOLは、Bで表されるboolean値をレジスタAにロードする.Bはfalseとtrueをそれぞれ0と1で表す.Cはboolean値も表し,Cが1であれば次の命令をスキップする.
local a = true;
1 [1] LOADBOOL 0 1 0
2 [1] RETURN 0 1
Cここでの役割は比較的特殊である.Cの具体的な用途を理解するには、まずluaの論理式と関係式がどのように処理されているかを知る必要があります.例えば、次のようにします.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
constants (2) for 0x80048eb0:
1 1
2 2
luaはLTとJMP命令を生成し,さらに2つのLOADBOOLを加えてaに対して異なるboolean値を与えることが分かる.LT(後述)命令自体はboolean結果値を生成するのではなく,後続のJMPに合わせてtrueとfalseの異なるジャンプを実現する.LTがtrueと評価された場合、実行は継続され、すなわちJMPに実行され、その後4に移行し、aにtrueが付与される.そうでなければ、次の命令をスキップして3行目に達し、aにfalseを付与し、次の命令をスキップする.上記のコードの実際の意味は次のように変換されます.
local a;
if 1 < 2 then
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変数宣言では、個別に値を割り当てる必要がなく、1つのLOADNIL命令を使用して完了できます.
状況について
local a;
local b = 0;
local c;
1 [1] LOADNIL 0 0
2 [2] LOADK 1 -1 ; 0
3 [3] LOADNIL 2 0
Lua 5.2では、aとcを1つのLOADNIL命令に統合することはできない.だから以上の書き方は理論的にもっと多くの命令を生成して、避けるべきで、書き換えるべきですlocal a,c;
local b = 0;