コンピュータの構成と設計 ~2.8節

パタヘネです。上下巻をかなりの金額で買ったのでしっかり読み進めていきたいと思います。

コンピュータの抽象化とテクノロジ

 本書はソフトウェアとハードウェアのインターフェースに関して記述する。ソフトウェアがどのような指示をハードウェアに出すか、ソフトウェアの性能は何によって決まり改善方法はなにか、並列処理に重点が置かれ始めているのはなぜか、ハードウェアのサポートはどのようになっているか等に関して学ぶことができる。

 コンピュータアーキテクチャの考え方の中心にある概念として、次の7つが挙げられる。

  • 抽象化
  • 一般的な場合を高速化
  • 並列処理
  • パイプライン処理
  • 予測による性能向上
  • 記憶階層
  • 冗長性による信頼向上

 コンピュータの性能の定義は複数の方法がある。代表的には、応答時間(実行時間)、スループットがある。実行時間がこの中でより本質的な性能となる。あるプログラムの実行時間は、そのプログラムが使用する1命令あたりの平均クロック・サイクル時間(CPI)と命令数、1クロック・サイクルあたりに掛かる時間(クロック・サイクル時間)の積で表される。個別のタームが大きい・小さいと議論することは意味がなく、実行時間全体で議論しないと性能の議論にならない。

MIPSアセンブリ言語

 コンピュータはバイナリデータを読み込んで命令として解釈し、内容に応じた処理をおこなう。コンピュータが理解できる命令のセットを命令セットという。命令セットにはARMv7、Intel x86、ARMv8などがある。本書ではMIPS命令セットを使用して解説していく。ただ、コンピュータができなければならない基本的な機能を命令セットによって実現する以上、どの命令セットも類似している。

 MIPS命令セットは演算命令に3つのオペランドを必ずとる。これは規則性を持たせることでハードウェア設計を単純にするためである。高級言語と異なり、算術命令ではハードウェア(CPU)が持っている記憶域であるレジスタを直接指定する必要がある。レジスタ長はMIPSでは32ビットであり、32ビットを1単位としてワードと呼ぶ。また、レジスタ数は32である。レジスタ数に制限があるのは小さければ小さいほど高速になるという設計原理を当てはめた結果である。レジスタはドルマーク$の後に2文字つけて表現する。たんに0~31までの数字にしないのは、それぞれのレジスタに役割があるためである。

 算術命令以外にはデータ転送命令がある。データ転送命令ではメモリ上のアドレスを指定する。MIPSでは語アドレスは4の倍数でなければならない。また、バイトオーダーはビッグエンディアンを使用する。データ転送命令にはロードとストアがある。

 命令の中には定数を使うものがある。これは、演算やデータ転送で定数を毎回メモリに取りに行くより命令に含ませている方が高速になり、使用するエネルギーが少なくて済むメリットが有るからである。

 コンピュータでは数値を2進数で取り扱う。歴史的には10進数を使ったコンピュータもあったが結局内部ではオンとオフの信号が使用されており、10進数を使用する非効率性から現在のコンピュータは2進数に基づく処理を行っている。現実の数値とコンピュータ上の数値の違いは、現実の数値の精度は無限であり、日常で扱うときは単に上位や下位の0を表示しないだけということである。コンピュータ上で用意されているビット幅に数値が収まらない場合、オーバーフローが発生したという。

 また、コンピュータ上で数値を表す際、符号の表現が問題となる。単純には1ビットを符号用に確保し、0だったら正、1だったら負と解釈する方法がある。この場合の問題は、計算後符号を設定するための回路を別に用意する必要があり無駄があること、正の0と負の0が存在すること、符号用のビットをMSBにするかLSBにするかの任意性があることなどが挙げられる。より扱いやすい方法として、2の補数表現がある。上位に1が詰まっている場合は負、そうでなければ正とする方式である。これは、ある数xと別の数yの減算を考えたとき、x-yが負になる場合、繰り下がり計算回路を下の桁から適用していくと自然と上位に1が詰まることになり、負の数が自動的に表せるという、ハードウェア上の実装の単純さのメリットが有る。ある数の正負の判定は最上位ビットを見れば良く、この最上位ビットを符号ビットとも呼ぶ。2の補数表現である数の符号反転を計算したいときは、その数の全ビットを反転し、1を加算すれば良い。短いビット数の数値を長いビット数に変換する場合、下の桁を元の数で埋め、残った桁を元の数の符号ビットで埋める。この操作を符号拡張という。

 コンピュータの命令自体2進数で表されており、add $t0,$s1,$s2といった命令も数値で表せる。MIPSでは命令が32ビットで表現され、その中はいくつかの区画(フィールド)に分けることができる。各フィールドで命令の種類、オペランドなどを表現している。命令を数値で表したものを機械語と呼ぶ。MIPSのR形式フィールドはop(6bit),rs,(5bit),rt(5bit),rd(5bit),shamt(5bit),funct(6bit)からなる。opはオペコード、rsは第一ソースオペランドレジスタ、rtは第二ソースオペランドレジスタ、rdはデスティネーションオペランドレジスタ、shamtはシフト量、functはオペコードと組み合わせて用いる機能コードである。一方、別形式もあり、I形式ではop,rs,rt,即値からなる。

MIPSの論理演算命令にはAND、OR、NORがある。NOTはNORの片方のオペランドを0とすることで表現する。

 条件分岐命令としてはbeq、bneがある。オペランドのレジスタ同士が等しい(異なる)場合は指定したラベルに移動するという意味になる。また、指定されたラベルに無条件で分岐するjという命令もある。ループは条件分岐や無条件分岐で実装できる。数値の大小は、まず比較用命令を行い、その結果のレジスタについてbeq、bneでジャンプする。また、case/switchは、レジスタに設定されているジャンプ先アドレスにジャンプするjrを使うことで実装できる。

 プロシージャを実装するための命令も存在する。プロシージャの実装では、引数をレジスタに入れる、呼び出し先にジャンプする、現在のレジスタ状態を保存する、処理をする、結果を入れる、レジスタを呼び出し前に戻す、呼び出し元の位置にジャンプするなどが必要となる。どのレジスタを引数に、どのレジスタを結果にするかが決まっており、$a0-$a3が引数、$v0-$v1が結果を入れるのに使われる。また、戻りアドレスを保存するため、現在アドレス+4をプログラムカウンタから$raに入れる命令jalが用意されている。引数が4つを超える場合は、メモリ上の領域を使って引数を渡す。引数を渡すとき、メモリ領域上のデータ形式としてスタックが採用されている。スタックの先頭を表すポインタを入れる$spがMIPSでは用意されている。また、プロシージャ内で破壊してはならないレジスタが決められており、それらを一時退避するためにもスタックが活用される。なぜスタックにするかというと、プロシージャがプロシージャを呼び出すことがあり、退避の順番と復帰の順番を考えると後入れ先出しとなるからである。また、スタックはレジスタに収まらないローカル変数を置く場所としても使われる。プロシージャからの復帰時にスタックの先頭はローカル変数を指し示している事があるため、スタック上のどこまでが復帰すべきレジスタなのか計算するのがやや複雑となる。$fpというレジスタはフレームポインタと呼ばれており、そのプロシージャが使用しているスタック領域の先頭を指し示すのに使われる。$fpと$spの間の領域をアクティベーションレコードといい、そのプロシージャが使っている領域となる。

 また、コンパイル時に決まらない量のメモリをプログラム中で動的に確保する事がある。このとき割当たる領域はヒープと呼ばれる。スタックはアドレス空間上のより大きなアドレスから小さなアドレスに伸長するのに対し、ヒープは小さなアドレスから大きなアドレスに伸長する。ヒープより小さなアドレス空間にはテキストセグメント(機械語コード)があり、更にそれより小さな空間は予約済みとなっている。

コメント

タイトルとURLをコピーしました