探索Lua 5.2内部実装:コンパイルシステム(2)ジャンプの処理
テキスト
プログラムを制御するための命令フローをジャンプします.Lua使用OP_JMP命令は、OP_についてジャンプを実行します.JMPの詳細については、『仮想マシン命令』を参照してください.ジャンプは条件ジャンプと非条件ジャンプに分けられる.非条件ジャンプは簡単ですが、ここから始めましょう.
goto
Lua 5.2では、gotoとlabelは、非条件ジャンプを実行するために新しく追加されたstatementである.この2つのstatementはそれぞれlparserにあります.cのgotostatとlabelstat関数で解析されます.前編で述べたように、グローバルデータDyndataにはgotoリストとlabelリストが保存されており、gotoとlabelは同じデータ構造Labeldescで表されている.
nameはlabelの名前を表し、互いに検索するために使用されます.labelである場合、pcは、このlabelに対応する現在の関数命令の集合の位置、すなわちジャンプする命令の位置を表す.gotoであれば、このgotoのために生成されたOP_を表すJMP命令の位置.nactvarは、このgotoまたはlabelを解析するときに、ジャンプ時にどのupvalueを閉じる必要があるかを決定するために、関数に有効なローカル変数がいくつあるかを表します.
gotostatはすでに生成されたOPを受け入れるJMP命令の位置は、まずnewlabelentryによってls->dyd->gtにLabeldescを生成し、未処理のgotoを表すためにfindlabelを呼び出してこのgotoを処理しようとする.findlabelは、現在のblockで定義されているlabelを検索します.見つかったらclosegotoを呼び出し、このgotoに対応するOP_をJMP命令のジャンプ位置はlabelのpcに設定し、OP_JMP命令で一部の局所変数に対応するupvalueを閉じる.見つからなかったら、このgoto対応のOP_JMP命令のジャンプ位置はNO_JUMPは、未決の位置を示し、後で処理するのを待つ.
gotostatの処理と同様に、labelstatはlabelを処理するときに、定義されているが未決のgotoを最初に検索します.gotoが見つかったら、そのOPも修正しますJMP命令のジャンプ位置.goto全体とlabel構文解析のコードは比較的直接的で、理解しにくいわけではありません.ここで難解なのはLua対OP_JMP命令の処理方法.OP_についてJMPの処理は,後の関係や論理演算,条件ジャンプにも用いられる.だから、Lua対OP_を理解JMP命令の処理は他のコンパイル部分を理解するための基礎である.
OP_JMP
前述したように,Luaはコンパイルであるため,本当に命令を生成する際には決定できないものが多い.例えばOP_JMP命令は、ジャンプする位置が定義されていない可能性がありますので、具体的なジャンプ位置がわかりません.Luaはこれらの命令を関数protoの命令リストに作成し、命令リストの位置を記録します.決定できる場合は、生成された命令をこの位置で見つけて修正します.これが命令埋め戻しです.
OP_についてJMP命令は、2つの命令パラメータを入力する必要があります:閉じるupvalue開始id AとジャンプオフセットsBx.luaK_を通過するとjump関数はOP_を生成しますJMP命令の場合、この命令のAは0に初期化され、sBxはNO_に初期化されるJUMPは、このOP_を表すintを返します.JMP命令の位置.この戻り値を保存すると、この命令が見つかり、埋め戻されます.
構文解析中(後述する関係や論理演算など)に一連のOP_が生成される可能性がある.JMP命令、彼らのジャンプ目標は一致して、それによって1つのジャンプ命令の集合を形成します.Luaはチェーンテーブル方式でこの命令セットを保存する.このチェーンテーブルはノードごとにOP_ですJMP命令自体は、sBxを使用してチェーンテーブルの次のOP_を指すJMP命令sBxがNO_の場合JUMPは、チェーンテーブルの末尾ノードを表します.ヘッドノードを指す位置が1つだけでOP全体を巡ることができますJMP指令チェーンテーブル.lcode.cではOP_JMP命令の処理関数は,いずれも1つのホップチェーンテーブルに基づいているが,場合によってはチェーンテーブルに1つのノードしかない.特定のジャンプアドレスを入力する必要がある場合は、チェーンテーブルを巡回し、各ノードのジャンプアドレスをターゲット位置に修正します.
埋め戻しジャンプアドレスは、次の2つの処理に分けられます.
1つはすでに生成された命令位置であり、Luaは直ちにこれらのジャンプ命令を目標位置に変更する.
もう1つは,現在位置,すなわち次に生成する命令の位置である.現在位置を処理する際、Luaは直ちに修正せず、現在のFuncStateのjpcチェーンテーブルに修正すべきジャンプ命令を直列に接続する.luaK_を使用する場合codeが次の命令を生成すると、まずdischargejpc関数が呼び出され、jpcチェーンテーブル上のすべてのジャンプ命令がこの位置に変更されます.これは、遅延埋め戻しに等しい.このように処理するのは、実はジャンプの最適化のためです.振り返るとluaK_jump関数はOP_を生成していますJMP命令の場合、現在のjpcが新たに生成されたjpcに直列接続され、jpcが空になります.この処理は実際にはOP_を生成するとJMP命令の場合、他のOP_があればJMP命令はここにジャンプする必要がありますが、実は新しく生成されたジャンプ命令のターゲット位置に間接的にジャンプすることに等しいです.これらのジャンプ命令はここにジャンプするのではなく、新しく生成されたOP_に直接ジャンプします.JMPのターゲット位置は、2ステップジャンプを1ステップにマージします.これらの命令は,新しく生成されたジャンプ命令のターゲットと一致するので,後で一緒に記入するのを待つジャンプ命令の集合に統合することができる.
次に,ジャンプに関連する命令生成apiを振り返る.
luaK_jumpは新しいジャンプ命令を生成するために使用されます.luaK_concat関数は、2つのチェーンテーブルを接続して1つのチェーンテーブルを合成するために使用されます.luaK_patchlistはチェーンテーブルのジャンプ位置を記入するために使用されます.luaK_patchtohereは、チェーンテーブルを現在の位置に記入する準備に使用します.luaK_patchcloseは、チェーンテーブルのA、すなわち閉じなければならないupvalue idを記入するために使用され、gotoの処理を参照することができます.
以上がLua処理ジャンプに関する内容です.ジャンプの処理は論理式と関係式とも密接に関連しており,後述する式の部分で詳細に説明する.
プログラムを制御するための命令フローをジャンプします.Lua使用OP_JMP命令は、OP_についてジャンプを実行します.JMPの詳細については、『仮想マシン命令』を参照してください.ジャンプは条件ジャンプと非条件ジャンプに分けられる.非条件ジャンプは簡単ですが、ここから始めましょう.
goto
Lua 5.2では、gotoとlabelは、非条件ジャンプを実行するために新しく追加されたstatementである.この2つのstatementはそれぞれlparserにあります.cのgotostatとlabelstat関数で解析されます.前編で述べたように、グローバルデータDyndataにはgotoリストとlabelリストが保存されており、gotoとlabelは同じデータ構造Labeldescで表されている.
/* description of pending goto statements and label statements */
typedef struct Labeldesc {
TString *name; /* label identifier */
int pc; /* position in code */
int line; /* line where it appeared */
lu_byte nactvar; /* local level where it appears in current block */
} Labeldesc;
nameはlabelの名前を表し、互いに検索するために使用されます.labelである場合、pcは、このlabelに対応する現在の関数命令の集合の位置、すなわちジャンプする命令の位置を表す.gotoであれば、このgotoのために生成されたOP_を表すJMP命令の位置.nactvarは、このgotoまたはlabelを解析するときに、ジャンプ時にどのupvalueを閉じる必要があるかを決定するために、関数に有効なローカル変数がいくつあるかを表します.
gotostatはすでに生成されたOPを受け入れるJMP命令の位置は、まずnewlabelentryによってls->dyd->gtにLabeldescを生成し、未処理のgotoを表すためにfindlabelを呼び出してこのgotoを処理しようとする.findlabelは、現在のblockで定義されているlabelを検索します.見つかったらclosegotoを呼び出し、このgotoに対応するOP_をJMP命令のジャンプ位置はlabelのpcに設定し、OP_JMP命令で一部の局所変数に対応するupvalueを閉じる.見つからなかったら、このgoto対応のOP_JMP命令のジャンプ位置はNO_JUMPは、未決の位置を示し、後で処理するのを待つ.
gotostatの処理と同様に、labelstatはlabelを処理するときに、定義されているが未決のgotoを最初に検索します.gotoが見つかったら、そのOPも修正しますJMP命令のジャンプ位置.goto全体とlabel構文解析のコードは比較的直接的で、理解しにくいわけではありません.ここで難解なのはLua対OP_JMP命令の処理方法.OP_についてJMPの処理は,後の関係や論理演算,条件ジャンプにも用いられる.だから、Lua対OP_を理解JMP命令の処理は他のコンパイル部分を理解するための基礎である.
OP_JMP
前述したように,Luaはコンパイルであるため,本当に命令を生成する際には決定できないものが多い.例えばOP_JMP命令は、ジャンプする位置が定義されていない可能性がありますので、具体的なジャンプ位置がわかりません.Luaはこれらの命令を関数protoの命令リストに作成し、命令リストの位置を記録します.決定できる場合は、生成された命令をこの位置で見つけて修正します.これが命令埋め戻しです.
OP_についてJMP命令は、2つの命令パラメータを入力する必要があります:閉じるupvalue開始id AとジャンプオフセットsBx.luaK_を通過するとjump関数はOP_を生成しますJMP命令の場合、この命令のAは0に初期化され、sBxはNO_に初期化されるJUMPは、このOP_を表すintを返します.JMP命令の位置.この戻り値を保存すると、この命令が見つかり、埋め戻されます.
構文解析中(後述する関係や論理演算など)に一連のOP_が生成される可能性がある.JMP命令、彼らのジャンプ目標は一致して、それによって1つのジャンプ命令の集合を形成します.Luaはチェーンテーブル方式でこの命令セットを保存する.このチェーンテーブルはノードごとにOP_ですJMP命令自体は、sBxを使用してチェーンテーブルの次のOP_を指すJMP命令sBxがNO_の場合JUMPは、チェーンテーブルの末尾ノードを表します.ヘッドノードを指す位置が1つだけでOP全体を巡ることができますJMP指令チェーンテーブル.lcode.cではOP_JMP命令の処理関数は,いずれも1つのホップチェーンテーブルに基づいているが,場合によってはチェーンテーブルに1つのノードしかない.特定のジャンプアドレスを入力する必要がある場合は、チェーンテーブルを巡回し、各ノードのジャンプアドレスをターゲット位置に修正します.
埋め戻しジャンプアドレスは、次の2つの処理に分けられます.
1つはすでに生成された命令位置であり、Luaは直ちにこれらのジャンプ命令を目標位置に変更する.
もう1つは,現在位置,すなわち次に生成する命令の位置である.現在位置を処理する際、Luaは直ちに修正せず、現在のFuncStateのjpcチェーンテーブルに修正すべきジャンプ命令を直列に接続する.luaK_を使用する場合codeが次の命令を生成すると、まずdischargejpc関数が呼び出され、jpcチェーンテーブル上のすべてのジャンプ命令がこの位置に変更されます.これは、遅延埋め戻しに等しい.このように処理するのは、実はジャンプの最適化のためです.振り返るとluaK_jump関数はOP_を生成していますJMP命令の場合、現在のjpcが新たに生成されたjpcに直列接続され、jpcが空になります.この処理は実際にはOP_を生成するとJMP命令の場合、他のOP_があればJMP命令はここにジャンプする必要がありますが、実は新しく生成されたジャンプ命令のターゲット位置に間接的にジャンプすることに等しいです.これらのジャンプ命令はここにジャンプするのではなく、新しく生成されたOP_に直接ジャンプします.JMPのターゲット位置は、2ステップジャンプを1ステップにマージします.これらの命令は,新しく生成されたジャンプ命令のターゲットと一致するので,後で一緒に記入するのを待つジャンプ命令の集合に統合することができる.
次に,ジャンプに関連する命令生成apiを振り返る.
luaK_jumpは新しいジャンプ命令を生成するために使用されます.luaK_concat関数は、2つのチェーンテーブルを接続して1つのチェーンテーブルを合成するために使用されます.luaK_patchlistはチェーンテーブルのジャンプ位置を記入するために使用されます.luaK_patchtohereは、チェーンテーブルを現在の位置に記入する準備に使用します.luaK_patchcloseは、チェーンテーブルのA、すなわち閉じなければならないupvalue idを記入するために使用され、gotoの処理を参照することができます.
以上がLua処理ジャンプに関する内容です.ジャンプの処理は論理式と関係式とも密接に関連しており,後述する式の部分で詳細に説明する.