レジスタ (コンピュータ)
レジスタ(register)はコンピュータのプロセッサなどが内蔵する記憶回路で、制御装置や演算装置や実行ユニットに直結した、操作に要する速度が最速の、比較的少量のものを指す。
一般に、論理回路において、フリップフロップなどにより状態を保持する装置をレジスタと呼ぶ。コンピュータにおいては、プロセッサが内蔵しているそれを指す。プロセッサには、命令セットで明示的に操作するレジスタ以外に、プロセッサ自身が動作するためのレジスタがあり、内部レジスタなどと呼ばれる。
論理回路において使われるレジスタという用語としては、たとえばレジスタ転送レベルなどがある。
プロセッサ内部のレジスタは、計算結果を一時的に保持したり、RAMやROMなどのメインメモリにアクセスする際のアドレスを保持したり、プロセッサや周辺機器の動作状態を保持・変更したりする。プロセッサの動作とは、極端にいえば、プログラムコードに従ってメインメモリとレジスタの間でデータを移送することだと表現できる。
プロセッサ内部にはたいてい数個から数十個のレジスタがあり、内部バスや演算回路などと密接に結びついているため、高速に動作する。
プロセッサについて「○○ビットプロセッサ(あるいは○○ビットCPU、○○ビットDSPなど)」と表現する場合、その目安の一つがレジスタの幅、特にそのプロセッサの汎用レジスタの幅である。また、MC68000のようにプロセッサ自体の設計は32ビットだが外部データバスは16ビット、といった設計の場合、「内部32ビット・外部16ビットCPU」のように言うこともある。こだわる人もいるが、そもそも厳密に定義できるわけでもないのでこだわる意味はない。
プロセッサの内部にどのようなレジスタがあるかということは、そのプロセッサの構造(アーキテクチャ)と命令セットに密接に関わる。
プロセッサ内部のレジスタには、用途に応じていくつかの種類がある。
通常、レジスタには、マニュアルなどで使われる英字1~3文字程度の略称がある。
特定の目的を持つレジスタ。
演算装置の出力と片方の入力につながった、ただ1個のレジスタがあり、そのレジスタに演算の結果を溜め込むことができる、といった古い設計のコンピュータで使われた語である。現在でもx86のAXレジスタのような多用されるレジスタを指して言うことがある。
A, ACCと略すことが多い。
演算結果を置いたり、データを一時的に記憶するレジスタである。プロセッサの内部バスにより演算装置とデータをやりとりできる。また、外部データバスとデータをやりとりできる。
メモリをアクセスする場合のアドレスを指定するときに用いるレジスタである。この内容を外部アドレスバスに出す事により、メインメモリからデータを読み出す。また、プロセッサによっては、アドレスを計算するための演算回路が付属しており、実効アドレスの計算を行なうことができるようになっていることもある。
アドレスレジスタの一種で、配列のベースアドレスを示すために利用できるレジスタである。配列データにアクセスするコードが簡単に記述できる。
配列のインデックスを指すために利用できるレジスタである。前述のベースレジスタが示すアドレスをベースとしたオフセットを指すようにして使う。あるいは配列の要素1個のサイズ分(たとえば4バイト整数の配列でインデックスが3ならオフセットは12)スケーリングすることができる。配列データにアクセスするコードが簡単に記述できる。アーキテクチャによっては、ベースレジスタの代わりにインデックスレジスタが配列の基点を指し、これにデータレジスタの値をスケーリングしてアクセスするものもある。
IXなどと略すことが多い。
アドレスレジスタの一種で、コールスタックの先頭を指すポインタレジスタである。これが示すアドレスの内容を読み出すと同時にアドレスを増やす、逆に、示すアドレスに書き込むと同時にアドレスを減らす、といった動作を行えるものが多い(特にCISCでは)。
また、このような、アドレスを参照してロードあるいはストアと同時にアドレスレジスタのインクリメント、デクリメントを行えるモードを「ポストインクリメント」・「プリデクリメント」、または「プリインクリメント」・「ポストデクリメント」と言う(プリとポストの組み合わせは、通常このどちらかになる)。
SPと略すことが多い。
アドレスレジスタの一種で、現在のサブルーチンを開始した時点のスタックポインタ(すなわち、コールスタック中の、現在のスタックフレームの位置)を指すようにして使うためのポインタレジスタである。スタックポインタ相対アドレッシングがあれば必ずしも必要ではないが、便利ではある。x86の場合、BPを利用したアドレッシングではセグメントレジスタにSSが選択される。
BPと略すことが多い。
アドレスレジスタの一種で、実行する命令のアドレスを指し示すレジスタ。命令の読み込みを行なう際にはその内容がアドレスバス上に出力され、また命令を読み込む度に読み取った命令の分だけ値が増加するようになっている。分岐命令は、このプログラムカウンタに値を代入することで実現される。また、スタックポインタと組み合わせ、プログラムカウンタの内容をスタックに一時保存した後に新たな値を代入すると、サブルーチンの呼出しを実現する事になる。反対に、スタックから値を取り出してプログラムカウンタに代入すると、サブルーチンから呼出し元に制御を復帰させる事になる。
PCと略すことが多い。また、インストラクションポインタ (IP)、逐次制御カウンタ (Sequential Control Counter; SCC)、あるいはロケーション・カウンタと称される場合もある。
何を書き込んでもその結果は保存されず、読み出すと常に全ビットがゼロである、というレジスタを持つプロセッサがある。一見意味がないようであるが、RISCアーキテクチャの様に命令セットを単純化した際に、演算命令とゼロレジスタを組み合わせて実質的に別の命令を実現できる。
例えば、加算命令において、A=B+Z (ここでA,Bは汎用レジスタとしZはゼロレジスタとする)に相当する命令をプログラムすると、Bの値がAに入り、BレジスタからAレジスタへの転送(MOVやLD)命令と等価になる。
また、比較命令は、演算そのものは減算命令と同じだが、その演算結果(大小関係や結果がゼロか否か)をフラグに反映させれば目的は達するが減算結果の数値は不要である。この場合に、Z=A-B に相当する命令をプログラムすると、減算に伴いフラグレジスタの値が更新される一方で、減算結果はどこにも保存されず、AレジスタとBレジスタの比較(CMP)命令と等価になる。
その他、アドレッシングにおいて必ずオフセットを指定する必要がある場合にゼロレジスタをオフセットに指定することで、オフセット無しでメモリにアクセスできる。
この様に、ゼロレジスタと演算命令を組み合わせることで、実質的に別の命令と同等の処理を行わせることができる。
読み出すと常に全ビットがゼロになっている R0 レジスタを持つMIPS がある一方で、読み出すと常に全ビットがゼロということではなく命令とオペランド位置との組み合わせによって、ゼロレジスタの内容が読めたり、ゼロとみなされたりするレジスタ r0 を持つPowerPCのようなプロセッサもある。
特定の目的を持たず、命令により各種機能を果たすレジスタ。特にデータレジスタとアドレスレジスタを兼ね備えたレジスタ。MC68000のように、データレジスタとアドレスレジスタを分けている例もあるが、ほとんどのプロセッサは汎用レジスタを持っている。また、汎用レジスタを多く用意しておけば、プログラミングの自由度が格段に増し、特にコンパイラが実行効率の良いオブジェクトコードを生成しやすくなる(多くの局所変数を汎用レジスタに割り当てることができる)。RISC系プロセッサでは全ての汎用レジスタが同等の機能を有している事が多い。そのような設計を「レジスタの直交性が高い」と表現する事がある。なお、プロセッサの設計においては、計算対象の指定方法がほぼ全ての命令で同じである(オペランドのアドレッシングが命令に依存しない)ことを「命令の直交性が高い」などと表現する事があるが、これはレジスタの直交性とは異なる概念である。
汎用レジスタの数はRISCでは多く、CISCでは少ないという差がある。2023年時点で最も有名といえるRISCアーキテクチャのARMとSISCアーキテクチャのx86では、32ビット版は16本と8本、64ビット版では31本と16本である。
ページング方式やセグメント方式による、ページセレクタやセグメントセレクタとして使うレジスタ。
演算結果によって生じた「桁あふれ」やアキュムレータが0であることの状態、あるいは各種のプロセッサの状態を保持するレジスタである。
割込み禁止状態(を設定できる)など「コントロールレジスタ」と言う方が的確な内容を含んでいる場合もある。
レジスタの値は、プロセッサの内部状態そのものである。つまり、レジスタの値をそっくりそのままどこかに退避させ、後にそれをそっくり元に戻せば、プロセッサの動作を一時中断し、他の作業をさせ、中断前の状態に戻すことができる。
割り込み処理による高速応答性を要求されるアプリケーションを作る場合や、時分割などによる擬似的なマルチタスクを実現する時には、この動作はきわめて頻繁に行なわれる。この、プロセッサの動作状態をそっくり保存して他の動作状態に入れ替えるという動作は、コンテキストスイッチと呼ばれる。
一般には、コンテキストスイッチはスタックを用いてレジスタの内容を外部のメインメモリの一定領域上に一時保存することで実現されている。しかし、コンテキストスイッチを高速化するために、主要なレジスタのコピーを保持する別のレジスタ群をプロセッサ内部に用意しておき、それを用いてコンテキストスイッチを行なう設計になっているプロセッサもある。つまり、プロセッサ内部のハードウェアにより、一瞬にしてアクセスするレジスタを切り替えられる。このようなレジスタ群を「レジスタセット」と呼ぶ。また、切り替えるレジスタ群が1セットしかない場合、それらのレジスタは「シャドーレジスタ」または「裏レジスタ」と呼ばれる。
レジスタセットを用いたコンテキストスイッチは、処理速度を飛躍的に上げられる画期的な手法だが、致命的な問題点がある。レジスタを構成する回路は複雑で、プロセッサ上に多数実装するのが難しい。このため、多数のタスクを切り替える一般的なOSでは、ごく軽い内容の割込みハンドラでのみ使う、といった使われ方がされている。
レジスタセットを持ったプロセッサで最も有名なものは、ザイログ社のCPU Z80であろう。ただし、セットの数は2つだけであり、一般には裏レジスタと呼ばれている。近年のプロセッサではARMがある。
ソフトウェア資産の有効活用を目的として、16ビットプロセッサの命令セットをそのまま動作できる32ビットプロセッサなどがしばしば開発される。
この場合、プロセッサ内部のレジスタのビット長は大きく(たいていの場合2倍に)なっているのだが、互換性を保つために古いCPUの命令コードで動作する場合には下位のビットしか用いない。
インテル社の8086系列のCPUは、このように拡張してきた経緯を持つ代表的なプロセッサである。8086CPUが誕生する前のインテルの8ビットCPU、8080では汎用レジスタを“a", “b", “c"…と名付けていた。これを拡張した8086の汎用レジスタは“ax", “bx", “cx"…となった(xはextendの略)。ところが、80386で32ビット化したため、レジスタの名前は“eax", “ebx", “ecx"…となった(eもextendの略)。さらに、AMD社がAMD64で64ビットに拡張した時には、レジスタ名は“rax", “rbx", “rcx"…となった。
プログラミング言語を機械語に変換する際に、式の評価途中の値などはいちいちメモリに書かず、レジスタに保持したまま計算を続けたほうが効率が良い。さらに、可能なら変数自体もメモリではなくレジスタに割り付けてしまえば、さらに効率が良い。
そのようなレジスタのやり繰りの問題をレジスタ割り付けと言い、コンパイラ最適化の重要なテーマの一つである。
初期のC言語コンパイラには、最適化にあまり力を入れていないものがあり、そのためレジスタに割り付ける変数をプログラマが指定するためのキーワードregisterがあった。レジスタ割り付けは柔軟におこなったほうが性能が高くなるのが普通であり、特定の変数をレジスタに張り付けることで性能が出るようなことは少なく、現在の多くの最適化コンパイラはregister指定を単に無視する(チェックの厳しいコンパイラの場合、アドレス演算子 & をregister指定された変数に対して使うと警告ないしエラーとなる)。C++においても同様であったが、C++11ではregisterキーワードは非推奨となり、C++17では削除された(ただし予約語としては残されたまま)。
サブルーチンとの情報のやりとり(引数と返り値)は、プログラムをモジュールに分割してコンパイルできるようにするために、あらかじめ定めておく必要があり、呼出規約と言う。呼出規約にはいろいろあるが、レジスタが多数あるマシンであれば、レジスタでやりとりするのが効率的であり、レジスタ渡しと言う。バークレーRISC(w:Berkeley RISC)に影響されたSPARCなどのRISCプロセッサやEPICアーキテクチャには、アーキテクチャ的にこれを支援するレジスタ・ウィンドウを備える。レジスタ渡しに対し、スタックで渡すものをスタック渡しと言う。引数をスタックに積んでサブルーチンを呼び出すが、返り値はレジスタ(アキュムレータ)で返すことが多い。(同じような「何々渡し」という語を使うが、評価戦略と直接の関係は無いので注意)
PCでポピュラーなx86の場合(w:X86 calling conventions)、IA-32およびそれより前のプロセッサでは、コンパイラの生成するコードではスタック渡しが多かったが、最適化の進んだコンパイラではMicrosoft Cの__fastcall構文でレジスタ渡しを明示したり、可能な限りレジスタ渡しをするコードを生成するLSI C-86もある。x64にはMicrosoft x64とSystem V AMD64の2種類があるが、どちらもレジスタ渡しが基本である。
可変長引数をレジスタ渡しするのは困難であるため、可変長引数の場合にはスタック渡しにすることも多い。但し、呼び出し規約等で引渡し方法をきちんと定義し、またそれに沿ったプログラミングを行わないと、齟齬が生じてプログラムの誤動作を引き起こす。例えばC言語において、ヘッダーを適切にincludeせずにプロトタイプ宣言なしでprintf関数を呼ぶと暴走し、hello worldすら動かない事例を引き起こす。
ペリフェラルデバイスのレジスタは、ペリフェラル(プログラマブル・カウンタや割り込み制御、シリアル通信ポートなどのハードウェア)の動作を設定したり、動作状況を読み出したりするためのレジスタである。これらのレジスタは上記のプロセッサ内のレジスタとは異なり、プロセッサからアクセスできるアドレス空間の一部に配置される。プロセッサの種類によってはメモリ空間とは別にI/O空間というアドレス空間が存在し、レジスタはここに配置される。I/O空間を持たないプロセッサの場合はメモリ空間にレジスタを配置して利用し、このことをメモリマップドI/Oと言う。レジスタの各ビットの動作や配置はペリフェラルデバイス毎に異なり、ハードウェアの動作をよく理解して操作する必要がある。
組み込み機器用のマイクロコントローラの場合、プロセッサだけでなく多数のペリフェラルデバイスも同一のチップ内に納められており、多くのレジスタがマイクロコントローラ内に存在する。