ショックを受けないでください。これは多くのテキストですが、詳細な情報を提供しないと、これが何であるかを実際に示すことはできません (そして、私の質問に実際には対応していない多くの回答が得られる可能性があります)。そして、これは間違いなく割り当てではありません(誰かが彼のコメントでばかげて主張したように)。
前提条件
この質問は、少なくともいくつかの前提条件が設定されていない限り、おそらくまったく答えられないため、前提条件は次のとおりです。
- 仮想マシン コードは解釈されます。JIT コンパイラーが存在することは禁止されていませんが、設計はインタープリターをターゲットにする必要があります。
- VM は、スタック ベースではなく、レジスタ ベースである必要があります。
- 答えは、レジスタの固定セットがあることも、それらの数に制限がないことも想定していない可能性があります。
さらに、「より良い」のより良い定義が必要です。考慮しなければならないプロパティがいくつかあります。
- ディスク上の VM コードのストレージ領域。もちろん、ここですべての最適化を破棄してコードを圧縮することもできますが、これは (2) に悪影響を及ぼします。
- デコード速度。コードを直接実行できるものに変換するのに時間がかかりすぎる場合、コードを保存する最善の方法は役に立ちません。
- メモリ内のストレージ スペース。このコードは、さらにデコードするかどうかに関係なく、直接実行できる必要がありますが、さらにデコードが必要な場合、このエンコードは実行中および命令が実行されるたびに行われます (コードのロード時に 1 回だけ行われるデコードは項目 2 にカウントされます)。
- コードの実行速度 (一般的なインタープリター手法を考慮)。
- VM の複雑さと、そのためのインタープリターを作成するのがいかに難しいか。
- VM 自体が必要とするリソースの量。(VM が実行するコードのサイズが 2 KB で、瞬きするよりも速く実行される場合、それは適切な設計ではありませんが、これを行うには 150 MB が必要であり、その起動時間はコードの実行時間よりもはるかに長くなります。実行されます)
ここで、多かれ少なかれオペコードが実際に意味することの例を示します。操作ごとに 1 つのオペコードが必要なため、オペコードの数が実際に設定されているように見える場合があります。ただし、それほど簡単ではありません。
同じ操作に対する複数のオペコード
のような操作ができます。
ADD R1, R2, R3
R1 と R2 の値を加算し、結果を R3 に書き込みます。ここで、次の特殊なケースを検討してください。
ADD R1, R2, R2
ADD R1, 1, R1
これらは、多くのアプリケーションで見られる一般的な操作です。既存のオペコードでそれらを表現できます (最後のオペコードがレジスタではなく int 値を持っているために別のオペコードが必要な場合を除く)。ただし、これらに対して特別なオペコードを作成することもできます。
ADD2 R1, R2
INC R1
前と同じ。メリットはどこ?ADD2 は 3 つではなく 2 つの引数のみを必要とし、INC は 1 つしか必要としません。したがって、これはディスク上および/またはメモリ内でよりコンパクトにエンコードできます。いずれかの形式を別の形式に変換することも簡単であるため、デコード ステップでこれらのステートメントを表現するために両方の方法の間で変換できます。ただし、どちらの形式が実行速度にどの程度影響するかはわかりません。
2 つのオペコードを 1 つに結合する
ここで、ADD_RRR (レジスターの R) とデータをレジスターにロードするための LOAD があるとします。
LOAD value, R2
ADD_RRR R1, R2, R3
これら 2 つのオペコードを使用して、コード全体で常にこのような構成を使用できます...または、それらを ADD_RMR (メモリの M) という名前の単一の新しいオペコードに結合できます。
ADD_RMR R1, value, R3
データ型とオペコード
ネイティブ型として 16 ビット整数と 32 ビット整数があるとします。レジスタは 32 ビットなので、どちらのデータ型にも適合します。2 つのレジスタを追加すると、データ型をパラメーターにすることができます。
ADD int16, R1, R2, R3
ADD int32, R1, R2, R3
たとえば、符号付き整数と符号なし整数についても同じことが言えます。そうすれば、ADD は 1 バイトの短いオペコードになり、別のバイト (または単に 4 ビット) が VM にレジスタの解釈方法を伝えます (レジスタは 16 ビットまたは 32 ビットの値を保持しますか)。または、型エンコーディングを破棄して、代わりに 2 つのオペコードを使用できます。
ADD16 R1, R2, R3
ADD32 R1, R2, R3
どちらもまったく同じだと言う人もいるかもしれません.16ビットのオペコードとして最初の方法を解釈するだけでうまくいきます. はい、しかし非常にナイーブなインタープリターは、かなり異なって見えるかもしれません。たとえば、オペコードごとに 1 つの関数があり、switch ステートメントを使用してディスパッチする場合 (最適な方法ではなく、関数呼び出しのオーバーヘッド、switch ステートメントも最適ではないことはわかっています)、2 つのオペコードは次のようになります。
case ADD16: add16(p1, p2, p3); break; // pX pointer to register
case ADD32: add32(p1, p2, p3); break;
各機能は、特定の種類の追加を中心にしています。ただし、2番目のものは次のようになります。
case ADD: add(type, p1, p2, p3); break;
// ...
// and the function
void add (enum Type type, Register p1, Register p2, Register p3)
{
switch (type) {
case INT16: //...
case INT32: // ...
}
}
サブスイッチをメイン スイッチに追加するか、サブ ディスパッチ テーブルをメイン ディスパッチ テーブルに追加します。もちろん、型が明示的であるかどうかに関係なく、インタープリターはどちらの方法でも実行できますが、オペコードの設計に応じて、どちらの方法も開発者にとってよりネイティブに感じられます。
メタ オペコード
より良い名前がないので、そのように呼びます。これらのオペコードは、それ自体ではまったく意味がなく、後続のオペコードの意味を変更するだけです。有名な WIDE 演算子のように:
ADD R1, R2, R3
WIDE
ADD R1, R2, R3
たとえば、2 番目のケースでは、レジスタは 16 ビット (したがって、より多くのアドレスを指定できます) で、最初のケースでは 8 ビットしかありません。あるいは、そのようなメタ オペコードを持たず、ADD および ADD_WIDE オペコードを持つことはできません。WIDE のようなメタ オペコードは、SUB_WIDE、MUL_WIDE などを持つことを避けます。これは、他のすべての通常のオペコードに常に WIDE を付加できるためです (常に 1 つのオペコードのみ)。不利な点は、オペコードだけでは無意味になることです。それがメタ オペコードであったかどうか、その前にオペコードを常にチェックする必要があります。さらに、VM はスレッドごとに余分な状態を保存し (たとえば、現在ワイド モードであるかどうかに関係なく)、次の命令の後に状態を再度削除する必要があります。CPU にもそのようなオペコードがあります (x86 LOCK オペコードなど)。
適切なトレードオフを見つける方法???
もちろん、オペコードが多いほど、スイッチ/ディスパッチテーブルが大きくなり、これらのコードをディスクまたはメモリで表現するために必要なビットが多くなります (ただし、データが保存されていないディスクにより効率的に格納できる場合があります)。 VM で直接実行できる必要があります)。また、VM はより複雑になり、より多くのコード行が必要になります。一方で、オペコードがより強力になります。複雑な式であっても、すべての式が 1 つのオペコードになるポイントに近づいています。
小さなオペコードを選択すると、VM のコーディングが容易になり、非常にコンパクトなオペコードにつながると思います。一方、単純なタスクを実行するには非常に多くのオペコードが必要になる可能性があり、あまり使用されない式はすべて、オペコードを使用できないため、ある種の(ネイティブ)関数呼び出しになります。
私はインターネット上のあらゆる種類の VM について多くのことを読みましたが、どちらの方向にも適切で公正なトレードオフを実際に行っている情報源はありませんでした。VM の設計は CPU の設計に似ています。オペコードがほとんどなく高速な CPU もありますが、これらの多くも必要です。また、多くのオペコードを持つ CPU があり、非常に遅いものもありますが、同じコードを表現するために必要なオペコードははるかに少なくなります。「オペコードが多いほど良い」CPU が消費者市場を完全に獲得し、「オペコードが少ないほど良い」CPU は、サーバー市場またはスーパー コンピューター ビジネスの一部でのみ生き残ることができるようです。VMはどうですか?