探索Lua 5.2内部実装:仮想マシン命令(8)LOOP


テキスト
Lua5.2種類のforループを除いて、その他の各種ループはすべて関係と論理命令を使って、JMP命令に協力して完成します.
local a = 0;
while(a < 10) do
    a = a + 1;
end
        1       [1]     LOADK           0 -1    ; 0
        2       [2]     LT              0 0 -2  ; - 10
        3       [2]     JMP             0 2     ; to 6
        4       [3]     ADD             0 0 -3  ; - 1
        5       [3]     JMP             0 -4    ; to 2
        6       [4]     RETURN          0 1
2行目はLTを用いてレジスタ0とオープン10を比較し、成立より小さい場合は3行目のJMPをスキップし、4行目のADD命令を実行し、aを1加算して5行目のJMPを実行し、2行目にジャンプして条件を再判断する.以下が成立しない場合は、次のJMP命令を直接実行し、6行目の終了にジャンプします.
forループについてLua 5.2 numeric for loopとgeneric for loopに対応する2つの専用命令を使用した.
name
args
desc
OP_FORLOOP
A sBx
R(A)+=R(A+2); if R(A) OP_FORPREP
A sBx
R(A)-=R(A+2); pc+=sBx
 local a;
 for i = 1, 10 do
     a = i;
 end
main <test.lua:0,0> (8 instructions at 0x80048eb0)
0+ params, 5 slots, 1 upvalue, 5 locals, 2 constants, 0 functions
        1       [1]     LOADNIL         0 0
        2       [2]     LOADK           1 -1    ; 1
        3       [2]     LOADK           2 -2    ; 10
        4       [2]     LOADK           3 -1    ; 1
        5       [2]     FORPREP         1 1     ; to 7
        6       [3]     MOVE            0 4
        7       [2]     FORLOOP         1 -2    ; to 6
        8       [4]     RETURN          0 1
constants (2) for 0x80048eb0:
        1       1
        2       10
locals (5) for 0x80048eb0:
        0       a       2       9
        1       (for index)     5       8
        2       (for limit)     5       8
        3       (for step)      5       8
        4       i       6       7
upvalues (1) for 0x80048eb0:
        0       _ENV    1       0
Numeric for loop内部では、「for index」、「for limit」、「for step」の3つのローカル変数を使用してループを制御します.「for index」は初期値とループカウンタを格納し、「for limit」は格納サイクル上限とし、「for step」は格納サイクルステップ長として使用します.上記のプログラムでは,3つの値はそれぞれ1,10,1である.この3つのローカル変数は使用者には見えません.コードを生成するlocalsテーブルにこの3つのローカル変数を見ることができます.彼らの有効範囲は5行目の8行目、つまりforサイクル全体です.もう1つ使用するローカル変数は,ユーザが自分で指定したカウンタであり,前例では「i」である.この局所変数の有効範囲は6~7行,すなわちループの内部であることがわかる.この変数は、ループのたびに「for index」変数に設定されて使用されます.
上記の例では2~4行の初期化サイクルで使用した3つの内部局所変数を用いた.5行目のFOREPREPは、このループを準備するために使用され、for indexからfor stepを減算し、7行目にジャンプします.7行目のFORLOPはfor indexにfor stepを追加し、for limitと比較します.for limit以下の場合は、iをfor indexに設定し、6行目に戻ります.そうでなければループを終了します.iは、真のループカウントには使用されず、ループのたびに真のカウンタfor indexの値が与えられるだけであるため、ループでiを変更してもループカウントに影響しないことがわかる.
name
args
desc
OP_TFORCALL
A C
R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
OP_TFORLOOP
A sBx
if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }
for i,v in 1,2,3 do
    a = 1;
end
main <test.lua:0,0> (8 instructions at 0x80048eb0)
0+ params, 6 slots, 1 upvalue, 5 locals, 4 constants, 0 functions
        1       [1]     LOADK           0 -1    ; 1
        2       [1]     LOADK           1 -2    ; 2
        3       [1]     LOADK           2 -3    ; 3
        4       [1]     JMP             0 1     ; to 6
        5       [2]     SETTABUP        0 -4 -1 ; _ENV "a" 1
        6       [1]     TFORCALL        0 2
        7       [1]     TFORLOOP        2 -3    ; to 5
        8       [3]     RETURN          0 1
constants (4) for 0x80048eb0:
        1       1
        2       2
        3       3
        4       "a"
locals (5) for 0x80048eb0:
        0       (for generator) 4       8
        1       (for state)     4       8
        2       (for control)   4       8
        3       i       5       6
        4       v       5       6
upvalues (1) for 0x80048eb0:
        0       _ENV    1       0

Generic for loop内部でも3つの局所変数を用いてループを制御し,それぞれ「for generator」、「for state」および「for control」です.for generatorは反復で使用されるclosureを格納するために使用され、反復のたびにこのclosureが呼び出されます.for stateとfor controlはfor generatorに渡される2つのパラメータを格納するために使用されます.Generic for loopはまた、for generatorの戻り値を格納するためにカスタムローカル変数i,vを使用します.
前例の1~3行はinの後の式リスト(1,2,3)を用いて3つの内部で使用される局所変数を初期化した.4行目のJMPは6行目に移行します.TFORCAL教用レジスタ0(for generator)のclosureは、for stateおよびfor controlに渡され、結果をカスタムローカル変数リストiおよびvに返す.7行目はTFOROLOOPを呼び出してループ条件判断を行い,iが空であるか否かを判断する.空でない場合は、iの値をfor controlに割り当て、5行目にジャンプしてループします.