Cairo バイトコード#
Cairo バイトコードは、Cairo プログラムを表すためのバイナリ形式です。それはフィールド要素のシーケンスで構成されており、各要素は整数値です。これらの要素は特定の順序で配置され、Cairo Runner が正しく実行できるようになっています。
フィールド要素以外にも、Cairo バイトコードには prog_start と prog_end という 2 つのインデックス値が含まれており、実行する計算のバイトコード内での開始位置と終了位置を指定します。これにより、Cairo Runner は実行するコードセグメントを正しく識別し、メモリにロードすることができます。
Cairo バイトコードは、Cairo ソースコードをコンパイルして生成されます。コンパイラはソースコードを等価なバイトコード表現に変換し、後続の実行のためにファイルに保存します。このコンパイルプロセスは cairo-compile コマンドを使用して行うことができます。
Cairo アセンブリコードとバイトコードの関係#
Cairo アセンブリコードは、Cairo プログラムの命令のシーケンスを表すための人間が読めるテキスト形式です。それは手動で作成することも、Cairo ソースコードをコンパイルして生成することもできます。Cairo バイトコードとは異なり、Cairo アセンブリコードはテキスト形式であり、読みやすく編集しやすいです。
実際の開発では、プログラマは通常、Cairo ソースコードを使用してプログラムを記述し、cairo-compile コマンドを使用してバイトコードにコンパイルします。ただし、特定のタスクには、手動で Cairo アセンブリコードを作成する方が便利または適している場合もあります。
Cairo バイトコードとの関係は、cairo-compile コマンドによって Cairo ソースコードを等価なバイトコード表現に変換し、後続の実行のためにファイルに保存することができます。一方、cairo-disasm コマンドを使用してバイトコードを人間が読めるアセンブリコード形式に変換することもできます。
結論として、実際の開発では、プログラマは通常、Cairo ソースコードを使用してプログラムを記述し、cairo-compile コマンドを使用してバイトコードに変換します。ただし、特定のタスクには、手動で Cairo アセンブリコードを作成する方が便利または適している場合もあります。
Cairo のメモリメカニズム#
Cairo では、メモリメカニズムは再配置可能なメモリセグメントを使用して実現されます。再配置可能なメモリセグメントは、プログラムの実行中に動的に割り当ておよび解放され、異なるプログラム間で共有することができる特殊なメモリ領域です。
Cairo Runner は、再配置可能なメモリセグメントを使用してプログラムのメモリを管理します。プログラムが新しいメモリを割り当てる必要がある場合、Cairo Runner はオペレーティングシステムから新しいメモリブロックを要求し、それを再配置可能なメモリセグメントに追加します。プログラムが特定のメモリブロックをもはや必要としない場合、Cairo Runner はそれを再配置可能なメモリセグメントから削除し、オペレーティングシステムに返します。
さらに、Cairo は使用されなくなったメモリを自動的に管理するためのガベージコレクションメカニズムもサポートしています。ガベージコレクタは、プログラムで使用されていないオブジェクトを定期的にスキャンし、参照されなくなったオブジェクトをマークして削除するために使用されます。
結論として、Cairo では、再配置可能なメモリセグメントとガベージコレクションメカニズムがそのメモリメカニズムの重要な構成要素となっています。これらのメカニズムにより、Cairo はメモリを動的に割り当てて解放し、使用されなくなったオブジェクトを自動的に管理することができます。
命令の実行フロー#
Cairo では、PC が指す命令を実行した後、Cairo Runner は命令のフラグに基づいて実行する操作と次の状態を決定します。具体的には、Cairo Runner は次の手順を実行します:
- 命令の読み取り:Cairo Runner はメモリから PC が指す命令を読み取ります。
- 命令のデコード:Cairo Runner は命令をデコードし、実行する操作と次の状態を決定します。
- 操作の実行:デコードされた操作に基づいて、Cairo Runner は対応する操作を実行します。たとえば、デコードされた操作が加算である場合、Cairo Runner は 2 つのレジスタの値を加算し、結果を別のレジスタに保存します。
- PC の更新:デコードされた次の状態に基づいて、Cairo Runner は PC レジスタを更新して次に実行する命令を指します。次の状態がジャンプである場合、PC はジャンプ先のアドレスに設定されます。それ以外の場合、PC は順次実行する次の命令を指すように増加します。
- ステップ 1〜4 を繰り返す:プログラムが終了するかエラーが発生するまで、上記の手順を繰り返します。
結論として、Cairo では、PC が指す命令を実行した後、Cairo Runner はデコードされたフラグに基づいて実行する操作と次の状態を決定し、各命令を順番に実行します。
Runner の命令デコード#
Cairo では、Runner の命令デコードのプロセスは次のようになります:
- 命令の読み取り:Runner はメモリから PC が指す命令を読み取ります。
- オペコードの解析:Runner は命令のオペコードを解析し、実行する操作のタイプを決定します。
- レジスタの解析:命令に含まれるレジスタ番号に基づいて、Runner は使用するレジスタを決定します。
- イミディエイト値の解析:命令にイミディエイト値(即値)が含まれる場合、Runner はそれを対応する値に解析します。
- 操作の実行:解析された操作のタイプ、レジスタ、イミディエイト値などに基づいて、Runner は対応する操作を実行します。
- PC の更新:デコードされた次の状態に基づいて、Runner は PC レジスタを更新して次に実行する命令を指します。次の状態がジャンプである場合、PC はジャンプ先のアドレスに設定されます。それ以外の場合、PC は順次実行する次の命令を指すように増加します。
結論として、Cairo では、Runner は各命令をデコードするために、命令に含まれるオペコード、レジスタ、イミディエイト値などの情報を解析し、解析された情報に基づいて対応する操作を実行します。
オペコード#
Cairo では、オペコード(Opcode)は各命令で操作の種類を表すために使用されるバイナリコードです。Cairo のオペコードには次のようなものがあります:
- 加算命令(ADD):2 つのレジスタの値を加算するために使用されます。
- 減算命令(SUB):2 つのレジスタの値を減算するために使用されます。
- 乗算命令(MUL):2 つのレジスタの値を乗算するために使用されます。
- 除算命令(DIV):2 つのレジスタの値を除算するために使用されます。
- 代入命令(MOV):1 つのレジスタの値を別のレジスタにコピーするために使用されます。
- ジャンプ命令(JMP):特定の操作を実行するためにプログラムの別の部分にジャンプするために使用されます。
- 条件付きジャンプ命令(JCC):条件に基づいてプログラムの異なる部分にジャンプして特定の操作を実行するために使用されます。条件は等しい、大きい、小さいなどの関係である場合があります。
- 関数呼び出し命令(CALL):プログラム内で定義された関数を呼び出し、引数を渡してから呼び出し元に戻ります。
- リターン命令(RET):関数呼び出しから戻り、呼び出し元の状態を復元します。
- 組み込み関数命令(BUILTIN):Cairo は文字列操作、数学関数などのいくつかの組み込み関数を提供しています。組み込み関数命令はこれらの組み込み関数を呼び出してコードを簡素化し、効率を向上させることができます。
- 例外処理命令(EXCEPTION):プログラムがエラーや例外の状況に遭遇した場合、Cairo は例外処理メカニズムを使用してこれらの例外をキャプチャして処理することができます。例外処理命令には例外のスロー(THROW)、例外のキャッチ(CATCH)、例外のクリア(CLEAR)などが含まれます。
- スタック操作命令(STACK):スタックはプログラムの実行中に一時的なデータを格納するために使用されるデータ構造です。