ARM オペコードは x86 オペコードと大きく異なりますか?
はい、そうです。異なるプロセッサファミリのすべての命令セットは完全に異なり、互換性がないと想定する必要があります。命令セットは最初に、次の 1 つ以上を指定するエンコーディングを定義します。
- 命令オペコード;
- アドレッシング モード。
- オペランドのサイズ;
- アドレスサイズ;
- オペランド自体。
エンコーディングはさらに、アドレス指定できるレジスタの数、下位互換性が必要かどうか、迅速にデコードできる必要があるかどうか、および命令がどれほど複雑になるかによって異なります。
複雑さについて: ARM 命令セットでは、特殊なロード/ストア命令を使用してすべてのオペランドをメモリからレジスタにロードし、レジスタからメモリに格納する必要がありますが、x86 命令は単一のメモリ アドレスをオペランドの 1 つとしてエンコードできるため、個別のロード/ストア命令。
次に、命令セット自体です。異なるプロセッサには、特定の状況に対処するための特殊な命令があります。2 つのプロセッサ ファミリが同じもの (add
命令など) に対して同じ命令を持っている場合でも、それらは非常に異なる方法でエンコードされ、セマンティクスがわずかに異なる場合があります。
ご覧のように、どの CPU 設計者もこれらすべての要素を決定できるため、異なるプロセッサ ファミリの命令セット アーキテクチャは完全に異なり、互換性がありません。
レジスタ、int/float/double、および SIMD は、異なるアーキテクチャでは非常に異なる概念ですか?
いいえ、とても似ています。最新のすべてのアーキテクチャにはレジスタがあり、整数を処理できます。ほとんどのアーキテクチャは、ある程度のサイズの IEEE 754 互換の浮動小数点命令を処理できます。たとえば、x86 アーキテクチャには、既知の 32 ビットまたは 64 ビットの浮動小数点値に合わせて切り捨てられた 80 ビットの浮動小数点値があります。SIMD 命令の背後にある考え方も、それをサポートするすべてのアーキテクチャで同じですが、多くのアーキテクチャではサポートされておらず、異なる要件や制限があります。
オペコードは、Windows、Mac、Unix などのすべてのプラットフォームで共通ですか?
3 つの Intel x86 システムがあり、1 つは Windows を実行し、1 つは Mac OS X を実行し、もう 1 つは Unix/Linuxを実行すると、同じプロセッサで実行されるため、オペコードはまったく同じです。ただし、オペレーティング システムはそれぞれ異なります。メモリ割り当て、グラフィックス、デバイス ドライバー インターフェイス、スレッド化などの多くの側面で、オペレーティング システム固有のコードが必要です。そのため、通常、Windows 用にコンパイルされた実行可能ファイルを Linux で実行することはできません。
PE 形式は、プロセッサでサポートされているオペコードの正確なセットを保存しますか?それとも、OS が CPU に一致するように変換するより一般的な形式ですか?
いいえ、PE 形式はオペコードのセットを保存しません。前に説明したように、異なるプロセッサ ファミリの命令セット アーキテクチャは、あまりにも異なっているため、これを実現することはできません。PE ファイルは通常、1 つの特定のプロセッサ ファミリおよびオペレーティング システム ファミリのマシン コードを格納し、そのようなプロセッサおよびオペレーティング システムでのみ実行されます。
ただし、例外が 1 つあります。.NET アセンブリも PE ファイルですが、プロセッサやオペレーティング システムに固有ではない一般的な命令が含まれています。このような PE ファイルは、他のシステムで「実行」できますが、直接実行することはできません。たとえば、Linuxのmonoは、そのような .NET アセンブリを実行できます。
EXE ファイルは、必要な命令セットの拡張機能 (3DNOW! や SSE/MMX など) をどのように示していますか?
実行可能ファイルはそれが構築された命令セットを示すことができますが ( Chris Dodd の回答を参照)、実行可能ファイルが必要な拡張機能を示すことができるとは思いません。ただし、実行可能コードを実行すると、そのような拡張子を検出できます。たとえば、x86 命令セットには、CPUID
その特定の CPU でサポートされているすべての拡張機能と機能を返す命令があります。実行可能ファイルはそれをテストし、プロセッサが要件を満たさない場合に中止します。
.NET とネイティブ コード
.NET アセンブリと、CIL (Common Intermediate Language) と呼ばれるその命令セットについて、ある程度はご存知のようです。各 CIL 命令は特定のエンコーディングに従い、そのオペランドに評価スタックを使用します。CIL 命令セットは、非常に一般的でハイレベルに保たれています。メソッドが(Windows では によってmscoree.dll
、Linux では によってmono
) 実行され、メソッドが呼び出されると、Just-In-Time (JIT) コンパイラはメソッドの CIL 命令を受け取り、それらをマシン コードにコンパイルします。オペレーティング システムとプロセッサ ファミリに応じて、コンパイラは使用するマシン命令とそのエンコード方法を決定する必要があります。コンパイルされた結果はメモリのどこかに保存されます。次にメソッドが呼び出されると、コードはコンパイルされたマシン コードに直接ジャンプし、ネイティブ実行可能ファイルと同じくらい効率的に実行できます。
ARM 命令はどのようにエンコードされますか?
私は ARM を使用したことはありませんが、ドキュメントをざっと見てみると、次のことがわかります。ARM 命令の長さは常に 32 ビットです。多くの例外的なエンコーディングがあります (分岐命令やコプロセッサ命令など) が、ARM 命令の一般的な形式は次のようになります。
31 28 27 26 25 21 20 16
+---+---+---+---+---+---+---+---+---+---+---+---+- --+---+---+---+--
| | 状態 | 0 | 0 |R/I| オペコード | さ | オペランド 1 | ...
+---+---+---+---+---+---+---+---+---+---+---+---+- --+---+---+---+--
12 0
--+---+---+---+---+---+---+---+---+---+---+---+--- +---+---+---+---+
... | 目的地 | オペランド 2 |
--+---+---+---+---+---+---+---+---+---+---+---+--- +---+---+---+---+
フィールドの意味は次のとおりです。
- 条件: 真の場合に命令が実行される条件。これは、Zero、Carry、Negative、および Overflow フラグを調べます。1110 に設定すると、命令は常に実行されます。
- R/I : 0 の場合、オペランド 2はレジスタです。1 の場合、オペランド 2は定数値です。
- Opcode : 命令のオペコード。
- S : 1 の場合、ゼロ、キャリー、ネガティブ、およびオーバーフロー フラグは、命令の結果に従って設定されます。
- Operand1 : 最初のオペランドとして使用されるレジスタのインデックス。
- Destination : デスティネーション オペランドとして使用されるレジスタのインデックス。
- オペランド 2 : 第 2 オペランド。R/Iが 0 の場合、レジスタのインデックス。R/Iが 1 の場合、符号なし 8 ビットの定数値。これらのいずれかに加えて、オペランド 2 のいくつかのビットは、値がシフト/回転されているかどうかを示します。
詳細については、知りたい特定の ARM バージョンのドキュメントを参照してください。この例では、このARM7TDMI-S データ シートの第 4 章を使用しました。
各 ARM 命令は、どんなに単純であっても、エンコードに 4 バイトかかることに注意してください。オーバーヘッドが発生する可能性があるため、最近の ARM プロセッサでは、 Thumbと呼ばれる代替の 16 ビット命令セットを使用できます。32 ビット命令セットが表現できるすべてのことを表現することはできませんが、サイズも半分です。
一方、x86-64 命令には可変長エンコーディングがあり、あらゆる種類の修飾子を使用して個々の命令の動作を調整します。ARM 命令を x86 および x86-64 命令のエンコード方法と比較したい場合は、私が OSDev.org に書いたx86-64 命令エンコードの記事を読む必要があります。
あなたの元の質問は非常に広いです。もっと知りたい場合は、調査を行い、知りたい特定の事柄で新しい質問を作成する必要があります。