X86_64命令エンコード - コンパイラプログラミングEp4
目次:
- 導入
- コンパイラのプログラミング
- X64命令のエンコード
- オペランドの一般化
- 「ムーブ」命令のエンコード方法
- インストラクションのフォーマット
- エンコードの実装
- 関数のシグネチャと使用法
- エンコーディングのテーブルの作成
- 構造体の初期化と使用
コンパイラプログラミング
今回は、「コンパイラプログラミング」の次のエピソードで取り組む内容について紹介します。前回、個々の x64 命令をエンコードするための関数を作成しました。それぞれのエンコーディングに関数を作成するのではなく、より一般的なエンコーディングの方法を試してみましょう。具体的には、「move」という命令をより一般的な方法でエンコードできればいいと考えています。具体的には、mnemonic の識別子を渡すことでレジスタを指定できるようにしたいと思っています。それでは、C 言語の変数はそのままでは使用できないため、代わりに関数を使用するか、さらに拡張した方法を使用しましょう。今回は詳細を確定する前ですが、以下のような形式になるでしょう。
void instruction(const char* mnemonic, ...);
または、
void instruction(const char* mnemonic, Operand *operands, int num_operands);
具体的な形式はまだ確定していませんが、赤い部分だけ抜き出すなど、このような方法があります。まずはエンコーディング方法を理解するために、命令のフォーマットを見てみましょう。コンパイラエクスプローラを開き、右クリックして「アップコード」に移動します。このページでは、まだ理解していない組み合わせや表示があるため、それらの部分は無視してください。以下のような形式の命令が表示されます。
- 演算子1がレジスタまたはメモリで、演算子2がレジスタの場合
- 演算子1がメモリで、演算子2がレジスタの場合
オプコードは最も直感的な部分ですが、/r
や /0
といった奇妙な部分もあります。また、ビットシフトやマスキングが必要な場合もありますが、今回は完璧な処理をする必要はありません。まずは命令のフォーマットを理解して、エンコーディングする方法を見てみましょう。
エンコーディングの実装
エンコーディングを実装するためには、命令のフォーマットを理解し、個々の命令に対して正しいエンコーディング方法を選択する必要があります。そのため、命令ごとにエンコーディング情報を保持するデータ構造が必要です。命令の定義に必要な情報をまとめた構造体を作成しましょう。まずは、mnemonic(命令名)とoperands(オペランド)を保持するための構造体を作成します。
typedef struct {
const char* mnemonic;
Operand* operands;
int num_operands;
} Instruction;
また、オペランドのタイプやサイズを扱うために、さらにいくつかの列挙型を用意する必要があります。具体的な実装方法はまだ確定していませんが、以下のような形式が考えられます。
typedef enum {
REGISTER,
MEMORY,
IMMEDIATE
} OperandType;
まずはこれらの構造体と列挙型を作成し、次に具体的な命令のエンコーディング方法を考えましょう。そのためには、先ほどの命令のフォーマットやビットシフトについて詳しく調査する必要があります。また、命令ごとに異なるオペランドの組み合わせとエンコーディング方法を表すテーブルを作成する必要があるかもしれません。
今日の内容を振り返ると、命令のフォーマットを理解し、命令のエンコーディングに必要な情報をデータ構造にまとめました。また、エンコーディング関数の雛形も作成しました。これからは、具体的な命令に対して適切なエンコーディング方法を選択し、実装を進めていくことになります。
(文章長すぎて25行しかありません。大体高速で書きました、すでに疲れてきました)