先週の復習
CPUのprogramming modelは、以下の要素からなる。
すべてのCPUに詳しくなる必要はないが、どれか一つの専門家にはなって
いたい所である。
どちらが優れているかという議論はあったが、現在では卵のとがっている
方から割るか、丸い方から割るかぐらいの違いしかないと認識されている
ようだ。
これらの問題は、普通のプログラミングでは考える必要はないが、
効率の良いMachine Languageを生成する場合には考慮する必要が
ある。
現在のプログラミングでは、配列(array)やリスト(list)、構造体(structure) のアクセスが重要なので、Addressing mode は、それを1命令で実現しやすい ように設計されているのが普通だ。だいたい図のようなAddressing mode を採用しているものが多い。
8bit CPU(8080)の拡張によってできたCPUなので、32bit modeでも それをかなり引きずっている。命令は8bitの可変長命 令である。80386によって、16bit/32bit切替と仮想記憶がサポート され爆発的な成功を納めた。最近のPentinum II では、内部でRISC 命令に変換してから実行するというようなことをしている。16bit ではregisterの役割が偏っていたが、32bitでは若干対称性が良く なっている。4本のAccumulator、4本のIndex register を持つ。
Addressing modeには以下のようなものがある。
Immediate movb al,$F0 constantをloadする extended movb al,[$F000] 指定されたaddressからloadする indexed movb al,[EAX+5] indexとoffsetで示されたaddress からload する accumulator offset indexed mov [EBX+EAX] accumulator offset indexed mov [EBX+EAX+5]さらに、x86には segement register というのがあり、それによっ てvirtual memory (メモリ空間) を切り替えることができる。しか し、普通はすべて同じメモリ空間が設定されている。他のCPUでも データ(data)とコード(code)は別空間にできるものが多い。
演算はレジスタとレジスタの間で行うことが多い。 もう少し詳しくは、インテル386のアセンブラ を参照すること。
非常に簡潔なアーキテクチャであるが、branchの後の一命令を実行する delayed slotをもっている。これは今では既に時代遅れだと言える。 むしろ、MIPSは、大域的なレジスタ割り当てを持つコンパイラ技術が 注目された。32本の汎用レジスタを持つが、半分を大域的な変数に 割り当てることが可能である。
Overrapped Register Windowは subroutine callの度に多数(7つか ら20程度)のregister fileを切り替える。register fileがいっぱ いになると、割り込みがかかりstackに書き出す。この処理が重い ので、compilerはregister file の切替をできるだけ遅らせなくて はならない。これは、register windowの狙いからすると本末転倒 である。save, restore 命令によりregister windowを切り替える ことになっている。
Delayed branch は、PowerPCと同様にbranchの際のパイプラインの 乱れを避けるためのもので、branchをすることになって一命令 だけbranchの次の命令を実行する。これを行うかどうかを指定する bit が opcode の中に存在する。しかし、一命令だけでパイプラインが 乱れないというわけでもなく、compiler が面倒をみるということならば、 PowerPCと同様のことをしなければならない。これも失敗した技術 だということができるだろう。
Addressing modeには以下のようなものがある。
%g0 %g0 は0に固定されている register addressing ld 3,%r0 indexとoffsetで示されたaddress からload する register addressing ld %r5,%r0Immediate addressing はなく、PowerPCと同様である。set, sethi という 疑似命令が用意されている。これはorで実現されている。演算はPowerPCと 同様3引き数である。
IBMPC は、pw上でcompile すれば良い。Sparc は、nirai, kanai 上でcompileすれば良い。MIPS は、PlayStation 用のcross compiler を使用する。 /usr/open/lectures/kono/compiler/mc-intel に太田昌宏氏による Micro-C を i386 用に修正したものがある。
gdb というdebuggerを使って、そのAssemberの実行を一命令づつ 調べることができる。
int a[10]; main() { return a[4]; }は、gcc -O -S test.c によって、(RISCの場合は-Oを付けた方がより 分かりやすいコードがでることが多い)
.file "test.c" .version "01.01" gcc2_compiled.: .text .align 4 .globl main .type main,@function main: pushl %ebp movl %esp,%ebp movl a+16,%eax leave ret .Lfe1: .size main,.Lfe1-main .comm a,40,4 .ident "GCC: (GNU) 2.8.1"という形にcompileされる。これをgdbで実行するには、
(gdb) b main Breakpoint 1 at 0x80483cf: file test.c, line 4. (gdb) r Starting program: /local/kono/public_html/lecture/1999/compiler/99-10-25/a.out Breakpoint 1, main () at test.c:4 4 return a[4]; (gdb) disass Dump of assembler code for function main: 0x80483ccとすれば良い。: pushl %ebp 0x80483cd : movl %esp,%ebp 0x80483cf : movl 0x80494e4,%eax 0x80483d4 : leave 0x80483d5 : ret End of assembler dump. (gdb) stepi 5 } (gdb) stepi 0x80483d5 in main () at test.c:5 5 } (gdb) p $eax $1 = 0 (gdb)
以下のprogram check_endian.c がある。
int check = 0x12345678; main() { char i, *ptr; ptr = (char *)✓ i = ptr[1]; return i; }このprogramをcompileしたassemblerを、i386, Sparc, MIPS のうち、少なくとも 2つ のCPUで表示 させて見よ。また、gdb で i にどのような値が入るかを 確認せよ。そのCPUは、Little-Endian か Big-Endian かを答えよ。 また、 trace の結果を、確認せよ。
Endian の変換はどのような時に必要になるか。どのようにすれば 実現できるか?
Unix には、Builtin のEndianの変換関数がある。
それを探し出せ。また、その実装がどうなっているかを調べよ。
(ヒント: man -k を使う)
Cの 様々な整数演算が、どのようなアセンブラにコンパイラによって変換されるか を -S フラグを使って調べよ。(最低、4則演算については調べること) singed, unsigned について調べると良い。
また、それらの計算がどう進むかをstep実行し、レジスタの中身を表示する ことによって示せ。
Subject: Report on Compiler consturction Lecture 10/25というSubjectのメールにして、kono@ie.u-ryukyu.ac.jp まで送ること。 また授業を受けなかったものは、課題を、
Subject: Practice on Compiler consturction Lecture 10/25というサブジェクトで送ること。