命令の種別とOPコードマップ
命令の機能や、対象とするデータサイズごとに異なるOPコードが必要であり、OPコード部分として使えるビット数により、実装できる命令の種類数が制限されるので、どのような命令を作りどのようにOPコードを割り当てるかというのが命令セットアーキテクチャ設計のポイントとなる。
次の図に、SPARCのOP部のエンコーディングとOP=2の場合のOP3のエンコーディングを示す。このような図をOPコードマップという。SPARCの場合、OP部は2ビットで、命令の大きな種別を示し、演算命令はOP=2、ロード、ストア命令はOP=3となっている。そして、6ビットのOP3部でそれぞれの種別の中の各命令を指定している。演算命令として、何故、ここに書かれた種類を選んだかというのは説明し難いが、色々なプログラムを分析したり、ユーザの意見を聞いたりして、このような命令があれば効率的に処理が行えるということで、これらの命令が選ばれている。
なお、OP=2が全ての演算命令を収容しているのに対して、OP=1はCALL命令ひとつしか定義されていない。これは CALL命令で32ビットのアドレス空間の任意のアドレスに分岐できるようにするためには、アドレス指定に30ビット(命令アドレス空間は32ビットであるが、全ての命令は4バイト長であるので、30ビットのアドレスで足りる)を必要とするためである。
SPARCの命令のOP部のエンコーディング(出典:The SPARC Architecture Manulal version 9) |
SPARCのOP=2の演算命令のOP3部のエンコーディング(出典:The SPARC Architecture Manulal version 9) |
また、類似した命令では制御信号も共通のものが多いので、OP3のマップに見られるように、類似した命令のOPコードは隣接した位置に割り付けるのが普通である。このような割付を行うと、命令をデコードするロジックが簡単になり、デコーダの速度も向上する。
余談であるが、JAVAのバイトコードはドキュメントに書かれた命令の順に、1番から順番にOPコードが割り振られている。ソフト屋さんのセンスから言うと、OPコードの値をインデックスとして各命令の処理ルーチンの先頭アドレスを書いた配列を使って分岐すれば、命令とOPコードの対応はどれでも同じということであろうが、JAVAバイトコードを直接実行するハードウェアを設計することになったハード屋さんは嘆息したのではないかと思う。
CISCアーキテクチャとRISCアーキテクチャ
IBMのSystem 360メインフレームは、シリーズを構成する大型機から小規模なマシンに至るまで、同じ命令セットが動作するという画期的なコンセプトを打ち出したマシンであるが、その実現を可能としたのはマイクロコードという方式である。マイクロコードはマシン語命令よりも低レベルのハードウェア制御命令でプログラムを作り、そのプログラムで命令アーキテクチャで定義されたマシン語の命令機能を実現する手法である。System 360では、豊富にハードウェアが使える大型機では命令機能をハードウェアで実現し、小規模マシンでは、複雑な命令機能はマイクロコードで実現するという設計がなされた。前に述べたようにVAXアーキテクチャは多種、複雑なアドレッシングが可能となっており、また、命令としても、文字列処理などの複雑な命令を持っている。このため、ハードワイヤードのハードウェアだけで実現することは難しく、マイクロコードでVAX命令セットを実現するというアプローチが用いられた。
しかしながら、コンパイラが生成するコードを見ると、VAXの複雑なアドレッシングを必要とするケースは少なく、無駄が多い。また、コンパイラの技術が進歩してくると、高級言語からマシン語を生成し、そのマシン語をハードウェアがマイクロコードに分解して実行するという二度手間を掛けなくても、直接、マイクロコードレベルの簡単な命令を生成した方が効率的であり、その方がハードウェアとしてもベーシックで簡素な命令セットのサポートで済むので、高性能なものが作れるという考え方が出てきた。
このようなコンピュータは、IBM 360やVAXのように複雑な命令や、複雑なアドレッシングモードを持たないのでReduced Instruction Set Computer(RISC)と称し、それに対比して、従来のコンピュータをComplex Instruction Set Computer(CISC)と呼んだのが、RISCとCISCという呼称の始まりである。
IBMのJohn Cockeによって開発された801コンピュータ(T.J.Watson研究所の801ビルに研究室があったので、そう呼ばれた)は1980年に完成し、最初のRISCプロセサと言われる。このプロセサは汎用のコンピュータとしては商品化されなかったが、メインフレームの入出力を担当するチャネルプロセサなどの各種のIBMの装置で使用されたという。
また、ヘネパタ本で有名なU.C.BerkeleyのDavid Patterson教授が1980年から開発を始めたBerkeley RISCや、Stanford大学のJohn Hennessy教授の開発したMIPSなどが代表的な初期のRISCアーキテクチャである。なお、Berkeley RISCは、その後、SunのSPARCアーキテクチャに大きな影響を与え、MIPSアーキテクチャはHennessy教授らがMIPS社を設立して商品化された。
また、開発当時はRISCという名称はなかったが、1964年にJim ThorntonとSeymour Crayが設計したCDC 6600は、今日のRISCの特徴の多くを備えており、これがRISCの元祖という意見もある。
RISC命令アーキテクチャの大きな特徴は、メモリアクセスはロード命令とストア命令だけが行い、その他の演算命令のオペランドはレジスタに限るという構造にしたことである。これにより、アドレッシングなどのために命令が長くなることが無くなり、固定長の命令になった。
また、命令のオペランドをレジスタに限定したことにより、演算命令の途中でメモリからのオペランドの読み込みを待つ必要が無くなり、すっきりとしたパイプラインで演算命令を処理できるようになり、ハードウェアの性能を向上することができるようになった。
固定長の命令に関しても、複数の命令を並列に処理する最近のプロセサでは、その利点が生きてきている。性能を向上させるために1サイクルに複数の命令を実行しようとしても、VAXのような可変長の命令の場合、最初の命令の各オペランド指定のバイト数を順次チェックしていかないと、次の命令がどこから始まるかが分からない。従って、複数の命令を同時並列的に解釈(デコード)するということが難しい。一方、大方のRISCの場合は、命令は32ビットの固定長であるので、次の命令の先頭は4バイト先と決まっており、複数の命令を並列にデコードすることは容易である。
勿論、VAX命令セットの場合も、3つのオペランド指定が全てRegisterやRegister Deferredなどの1バイト長であり、命令は4バイト長と仮定して複数の命令を並列にデコードすることは可能である。そして、命令のデコードと並行して1バイト長でないオペランド指定が混じっていないかどうかをチェックし、全てのオペランド指定が1バイトと確認できれば、並列に処理を進める。一方、1バイト長でないオペランド指定が含まれている場合は、最初の1命令だけをデコードして、次の命令の先頭を見つけるという処理を行えばよい。このようなやり方でも、1バイト以外のオペランド指定の使用頻度が少なければ、効果がある。
Intelのx86アーキテクチャも可変長の命令アーキテクチャであり、命令の長さを判定して次の命令の先頭を見つけることが難しい。このため、次やその次の命令の開始位置が比較的容易に見つけられるバリエーションの場合には並列処理を行い、複雑で長さが容易に分からない命令が入ってくると、その場合は1命令だけを処理するというやり方を行っている。
また、最近のIntelプロセサでは、マシン語の命令をMicroOPと呼ぶRISCレベルの内部命令に変換してから処理を行っており、命令の実行部分のパイプラインはRISCプロセサと変わらない。ということで、Intelプロセサは容易に長さの判別できる種類の命令だけを使い、それらを並列的にデコードしてMicroOPに変換して、RISCと同じパイプラインで処理できるというバイナリコードの場合には性能が出るという作りであり、レガシーのバイナリでは必ずしも、期待したほどの性能向上は得られない。
つまり、x86命令アーキテクチャの一部の命令だけが実質的には有効に利用できる命令であり、並列デコードが難しい命令は、Winchester Mystery Houseの行き止まりの階段化(あるいは盲腸化)しつつあるというのが筆者の見方で、その意味では、技術的にはRISCの勝利と言える。しかし、プロセサの中身がどのように作られているかを云々しなければ、IBM PC以来の膨大なソフトウェアの蓄積を持ちPC市場を押さえているCISCのx86アーキテクチャが、商業的には勝利しているという状況である。
まとめると、CISC命令アーキテクチャのプロセサでも、マイクロアーキテクチャ的にはRISCと変わらない構造になってきており、どちらが優れているかという議論は、殆ど意味が無くなってきている。固定長のRISC命令アーキテクチャの方がデコーダの設計は容易であるが、CISC命令アーキテクチャの高いコード密度が有利な分野もあり、一概にどちらが良いとは言えず、用途によって優劣が変わる。
また、最近のx86プロセサのように、CISCでも使用頻度の高い簡単な命令の並列デコードを行ったり、RISCでもARMのThumb命令のような16ビット命令を作るというような拡張を行ったりすれば、どちらも欠点の大部分をカバーすることが可能であり、どちらでも良いとも言える。というところであろうか。