12

いくつかの手順とそれに対応するエンコーディングを次に示します。

55                      push   %ebp
89 e5                   mov    %esp,%ebp
83 ec 18                sub    $0x18,%esp
a1 0c 9f 04 08          mov    0x8049f0c,%eax
85 c0                   test   %eax,%eax
74 12                   je     80484b1 <frame_dummy+0x21>
b8 00 00 00 00          mov    $0x0,%eax
85 c0                   test   %eax,%eax
74 09                   je     80484b1 <frame_dummy+0x21>
c7 04 24 0c 9f 04 08    movl   $0x8049f0c,(%esp)

今日のマイクロプロセッサは32ビットまたは64ビットであることが多く、通常は4バイトまたは8バイトのチャンクでメモリからデータを読み取ると思います。ただし、命令の長さは可変にすることができます。マイクロプロセッサはこれらの命令をどのようにデコードし、実装を容易にするために一定の長さではないのはなぜですか?

4

4 に答える 4

11

命令長を固定するのには非常に理由があり、実装の単純さが最も重要です。そのため、 RISCプロセッサや多くの初期のコンピュータのように、多くのプロセッサの命令長は固定されています。

x86のようなCISC命令セットは、マイクロコードによって順次(段階的に)デコードされるように設計されています。(マイクロコードはCISC命令の一種のインタープリターと考えることができます)これは、x86が設計された80年代初頭の最先端技術でした。

マイクロコードが死んでいるので、今日これは問題です。x86命令は、RISC命令とは異なり、より小さなµ-opsに分割されるようになりました。ただし、そのためには、x86命令を最初にデコードする必要があります。また、現在のCPUは、各サイクルで最大4つの命令をデコードします。命令を次々と順番にデコードする時間がないため、これはブルートフォースだけで機能します。命令キャッシュからラインが取り込まれると、多くのデコーダーがラインを並列にデコードします。可能なバイトオフセットごとに1つの命令デコーダ。デオコーディング後、各命令の長さがわかり、プロセッサはどのデコーダが実際に有効な命令を提供するかを決定します。これは無駄ですが、非常に高速です。

可変の命令サイズは、より多くの問題を引き起こします。たとえば、命令は2つのキャッシュラインまたはメモリ内の2つのページにまたがることができます。だからあなたの観察は的確です。今日、x86のようなCISC命令セットを設計する人は誰もいません。ただし、一部のRISCは最近、よりコンパクトなコードを取得するために2番目の命令サイズを導入しました:MIPS16、ARM-Thumbなど。

于 2011-11-21T00:08:23.753 に答える
7

編集:それをより読みやすくすることを望んでいます。

ハードウェアは、メモリを組織化されていないバイトの長いリストとは見なしません。固定ワード長または可変ワード長のすべてのプロセッサには、特定のブート方法があります。通常、ブートコードの最初の命令へのアドレスまたは最初の命令自体のいずれかを持つプロセッサメモリ/アドレス空間内の既知のアドレス。そこから、すべての命令について、現在の命令のアドレスがデコードを開始する場所になります。

たとえば、x86の場合、最初のバイトを確認する必要があります。そのバイトのデコードによっては、さらにオペコードバイトを読み取る必要がある場合があります。命令がアドレス、オフセット、またはその他の形式の即時値を必要とする場合、それらのバイトもそこにあります。プロセッサは非常に迅速に、この命令のバイト数を正確に認識します。デコードにより、命令に5バイトが含まれ、アドレス0x10で開始されたことが示されている場合、次の命令は0x10+5または0x15にあります。これは永遠に続きます。無条件分岐は、プロセッサによってさまざまな種類がありますが、命令に続くバイトが別の命令であるとは限りません。条件付きまたは無条件の分岐は、別の命令または一連の命令がメモリ内で開始する手がかりを提供します。

今日のX86は、命令をデコードするときに一度に1バイトをフェッチしないことに注意してください。適切なサイズの読み取りが発生し、おそらく一度に64ビットであり、プロセッサは必要に応じてバイトをそこから引き出します。最新のプロセッサから1バイトを読み取る場合、メモリバスはフルサイズの読み取りを実行し、メモリコントローラが後のビットのみをプルするバス上のすべてのビットを表示するか、そのデータを保持するまでになります。 。連続したアドレスに2つの32ビット読み取り命令があるが、メモリインターフェイスで1つの64ビット読み取りのみが発生するプロセッサがいくつかあります。

逆アセンブラやエミュレータを作成することを強くお勧めします。固定長の命令の場合、それは非常に簡単です。最初から始めて、メモリを通過するときにデコードするだけです。固定ワード長の逆アセンブラは、このプロセスの一部であるデコード命令について学習するのに役立つ場合がありますが、可変ワード長の命令に従う方法と、整列から外れることなくそれらを分離する方法を理解するのに役立ちません。

MSP430は、最初の逆アセンブラとして適しています。gnuツールasmやCなど(さらに言えばllvm)があります。アセンブラから始めてCにするか、事前に作成されたバイナリをいくつか使用します。重要なのは、プロセッサのようにコードをウォークスルーし、リセットベクトルから始めて、ウォークスルーする必要があるということです。1つの命令をデコードすると、その長さがわかり、無条件分岐に到達するまで次の命令がどこにあるかがわかります。プログラマーが逆アセンブラをだますために意図的にトラップを残していない限り、すべてのブランチが条件付きまたは無条件で有効な命令を指していると想定してください。午後または夕方は、すべてを打ち負かすか、少なくとも概念を理解するために必要なすべてです。必ずしも命令を完全にデコードする必要はありません。これを本格的な逆アセンブラにする必要はありません。命令の長さを決定し、それがブランチであるかどうか、そしてブランチである場合はどこであるかを決定するのに十分なだけデコードする必要があります。16ビット命令であるため、必要に応じて、可能なすべての命令ビットの組み合わせとその長さのテーブルを一度に作成できます。これにより、時間を節約できます。あなたはまだ枝を通り抜ける道を解読しなければなりません。

一部の人々は再帰を使用するかもしれません、代わりに私はどのバイトが命令の始まりであるか、どのバイト/ワードが命令の一部であるが最初のバイト/ワードではないか、そして私がまだデコードしていないバイトを示すメモリマップを使用します。まず、割り込みとリセットのベクトルを取得し、それらを使用して命令の開始点をマークします。次に、命令をデコードしてさらに開始点を探すループに入ります。パスが他の開始点なしで発生した場合、私はそのフェーズを終了しました。いずれかの時点で、命令の途中にある命令の開始点を見つけた場合、解決するには人間の介入が必要な問題があります。たとえば、古いビデオゲームのROMを逆アセンブルすると、手書きのアセンブラーが表示される可能性があります。コンパイラによって生成された命令は、非常にクリーンで予測可能である傾向があります。命令のクリーンなメモリマップと残っているものでこれを乗り越えれば(データを想定)、命令がどこにあるかを知って1つのパスを作成し、それらをデコードして印刷できます。可変ワード長命令セットの逆アセンブラが実行できないことは、すべての命令を見つけることです。命令セットに、たとえばジャンプテーブルや、実行用の実行時計算アドレスがある場合、実際にコードを実行しないと、それらすべてを見つけることはできません。

そこには多くの既存のエミュレーターと逆アセンブラーがあります。自分で作成するのではなく、従うことを試みたい場合は、私自身http://github.com/dwelch67をいくつか持っています。

可変および固定の単語長には賛否両論があります。Fixedには確かに利点があり、読みやすく、デコードしやすく、すべてが素晴らしく適切ですが、RAM、特にキャッシュについて考えると、ARMと同じキャッシュにさらに多くのx86命令を詰め込むことができます。一方、ARMははるかに簡単にデコードでき、ロジックや電力などがはるかに少なくて済みます。歴史的に、メモリは高価でしたロジックは高価でした、そしてあなたが行くにつれてバイトはそれがどのように機能したかでした。1バイトのオペコードでは256命令に制限されていたため、とにかく可変ワード長にしたイミディエートとアドレスは言うまでもなく、より多くのバイトを必要とする一部のオペコードに拡張されました。何十年にもわたって下位互換性を維持すると、現在の場所に行き着きます。

この混乱のすべてに追加するために、たとえばARMには可変ワード長の命令セットがあります。Thumbには単一の可変ワード命令であるブランチがありましたが、これは固定長として簡単にデコードできます。しかし、彼らは、可変ワード長の命令セットに実際に似ているthumb2を作成しました。また、32ビットARM命令をサポートするプロセッサの多く/ほとんどは16ビットサム命令もサポートしているため、ARMプロセッサを使用しても、データをワードごとに整列してデコードすることはできず、可変ワード長を使用する必要があります。さらに悪いことに、ARMからサムへの遷移は実行によってデコードされます。通常、単純に分解してサムからアームを把握することはできません。混合モードのコンパイラで生成された分岐では、分岐するアドレスをレジスタにロードしてから、bx命令を使用して分岐することがよくあります。

于 2011-11-20T21:05:26.107 に答える
4

それらがどのように正確にデコードされるかについては答えることができませんが、なぜそれらが可変長であるかについては答えることができます。

長さが可変である理由は、コードサイズを小さく保ちたいという願望と、予期しない命令セットの拡張の両方によるものです。


命令サイズの縮小

一部の命令(本質的に)は、エンコードするためにより多くのスペースを必要とします。すべての命令がこれらに対応するのに十分な大きさの固定長に設定されている場合、命令コードには多くの無駄なスペースがあります。可変長命令を使用すると、命令をより小さなサイズに「圧縮」できます。


(予期しない)命令セット拡張

もう1つの理由は、命令セットの拡張です。元々、x86には256個のオペコードしかありませんでした。(1バイト)次に、命令を追加する必要があったため、1つの命令を破棄し、そのオペコードを新しいオペコードのエスケープ文字として使用しました。その結果、新しい命令の方が長くなりました。しかし、それが命令セットを拡張し、下位互換性を維持する唯一の方法でした。

プロセッサがこれらをデコードする方法に関しては、それは複雑なプロセスです。命令ごとに、プロセッサは長さを見つけてそこからデコードする必要があります。これは、一般的なパフォーマンスのボトルネックである本質的に順次デコードプロセスにつながります。

最新のx86プロセッサには、uop(micro-op)キャッシュと呼ばれるものがあり、デコードされた命令をプロセッサによってより管理しやすい(そしてRISCのような)ものにキャッシュします。

于 2011-11-20T19:40:47.907 に答える
2

RISCを再発明しました

うーん、古典的なx86 (CISCを参照)に対してあなたがしている異議は、まさにRISC CPUアーキテクチャの設計者が、単純で、整列された、固定サイズの命令セットアーキテクチャを作成する動機となったものです。

最近のx86は、実際には、ユーザーに表示されるISAを、内部キャッシュに存在するよりRISCのようなマイクロオペレーションストリームに変換します。

良い観察。


ノート。
1.マイクロオペレーションは利用可能なテクニックの1つにすぎません。一般的なケースでは、命令のデコードとアラインメントが1つ以上のパイプラインステージで行われる限り、実際にかかった時間は平均命令実行時間に追加されません。分岐予測が機能していて、パイプラインがいっぱいに保たれている場合、命令のデコードと整列にかかる余分な時間は、実際の命令操作と並行して実行されるロジックによって処理されます。今日の設計者は何百万ものゲートを利用できるため、複雑なx86ISAのデコードに多くのロジックを割り当てることができます。
2.メモリバスの幅について言及しました。また、メモリパスは通常32ビットまたは64ビットよりも大きいことがわかります。アーキテクチャワードサイズは、単にALUとポインタサイズを指します。メモリとキャッシュインターフェイスの実際の幅は、多くの場合、アーキテクチャワードサイズの2倍または4倍です。

于 2011-11-20T19:42:03.557 に答える