2005年度 計算機システム演習 第1回

2006年度
計算機システム演習
第4回
2005年5月19日
caller-save (前回の方法)

caller-save (呼び出し元で
保存)


foo:
addi $sp, $sp, -8
:
# $t0に代入
:
sw
$t0, 4($sp)
jal
bar
lw
$t0, 4($sp)
:
# $t0を使った処理
:
addi $sp, $sp, 8
jr
$ra
レジスタ $t0〜$t9
データが入っているレジスタ
はサブルーチンの呼び出し元
で待避・復帰
 サブルーチン内では $t0〜
$t9 を自由に使える
bar:
# $t0を使った処理
callee-save

callee-save (呼び出し先で
保存)


レジスタ $s0〜$s7
サブルーチンで使うレジスタ
はサブルーチンの先頭・最後
で待避・復帰
 呼び出し元では $s0〜$s7
を自由に使える
 $ra も callee-save
foo:
jal
bar
bar:
addi $sp, $sp, -4
sw
$s0, 0($sp)
:
# $s0を使った処理
:
lw
$s0, 0($sp)
addi $sp, $sp, 4
jr
$ra
効率の違い

レジスタを退避・復帰する回数が変わる
プログラム
# $xに代入
jal foo
jal bar
# $xを使って計算
# $xに代入
jal foo
# $xを使って計算
jal bar
# $xを使って計算
caller-save
$xはfooの前で退避し、barの
後で復帰(1回)
$xはfooとbarの前後で保存・
復帰(2回)
callee-save
$xの退避・復帰はfooとbarで $xの退避・復帰はfooとbarで
使うかどうかに依存(0〜2回) 使うかどうかに依存(0〜2回)
例外とは?

Java の例外

自分で書いた catch 文の
中の処理が行われる

CPU の例外

OSカーネル内で用意され
た処理が行われる
プログラム
例外発生
try {
:
例外発生
:
} catch (Exception e) {
例外処理
}
例外処理
OSカーネル
例外の種類

プログラムの不正実行で発生するもの

不正アドレスのアクセス



例: lw $t0, 0x00000001
Exception 4 [Address error in inst/data fetch]
プログラムから明示的に発生させるもの

OSカーネルでしか使えない
機能を利用するため


ファイル入出力など
サブルーチンに似ているが
サブルーチンでは呼べない
例外番号 内容
8
syscall 例外
13
トラップ例外
例外処理の流れ
プログラム
:
li $v0, 5
syscall
move $t0, $v0
:

例外発生
復帰
例外ハンドラ
read_int の処理
OSカーネル
例外ハンドラ


アドレス 0x80000180
から実行される
$k0, $k1 レジスタが使
える
.kdata
:
.ktext 0x80000180
:
レジスタの保存

例外ハンドラ内で使うレジスタを保存

例外処理がプログラムの実行に悪影響を与えないよ
うにする

callee-save
.kdata
save_t0:
.word 0
.ktext 0x80000180
.set noat
move $k1, $at
.set at
sw
$t0, save_t0
# $t0を保存するメモリ領域
#
#
#
#
$atを扱うことを許可する
疑似命令が使うレジスタを$k1に保存
$atを扱うことを不許可にする
$t0を使うなら元の値を保存
疑似命令(再)

MIPS にはない命令


blt, bge, ...
命令自体はあるがオペランドが違う命令

sw $t0, A($t0)
lui $at, 4097
addu $at, $at, $t0
sw
$t1, 0($at)
$at レジスタを使って実現
ここで例外が発生して $at が
上書きされると困る
コプロセッサ

メインプロセッサとは別のプロセッサが例外を管
理する


例外番号など
コプロセッサのレジスタには mfc0, mtc0 命令で
アクセスできる

mfc0 CPUレジスタ, コプロセッサレジスタ


コプロセッサのレジスタの内容をCPUレジスタにコピー
mtc0 CPUレジスタ, コプロセッサレジスタ

CPUレジスタの内容をコプロセッサのレジスタにコピー
例外の種類の判定

コプロセッサの Cause レジスタに発生した例外
番号が入っている

$13 レジスタ
例外コード
mfc0 $k0, $13
srl $k0, $k0, 2
andi $k0, $k0, 0x1f
# 2ビット右シフト
# 下位5ビットを取り出す
実際の例外処理

例外に応じた処理をする

不正メモリアクセス



エラーを表示
プログラムを終了
syscall

$v0 で指定されたサービスを実行


$a0〜$a3を引数とする
$v0 に結果を代入
PC の調整

次の命令から実行再開

EPC(Exception Program Counter)レジスタに例外
が発生した命令のアドレスが入っている


コプロセッサの $14 レジスタ
EPC の値を 4 増やせば次の命令から再開できる
mfc0 $k0, $14
addiu $k0, $k0, 4
mtc0 $k0, $14
レジスタの復帰と例外ハンドラ終了

例外発生時と全てのレジスタの内容を同じにし
ておく必要がある


syscall の場合は $v0 だけ変わる
eret 命令で EPC レジスタが指すアドレスから実
行を再開
lw
$t0, save_t0
.set noat
move $at, $k1
.set at
eret
# $t0を復帰
# $atを扱うことを許可
# $atを復帰
SPIM での例外ハンドラ
 デフォルトで
/usr/local/share/exceptions.s が
使われている

変更するには -exception_file オプションをつけて
xspim を起動すればよい


例: xspim -exception_file myexceptions.s
Loaded: myexceptions.s と表示される
スタートアップルーチン

myexceptions.s には main ルーチンを呼び出
す初期ルーチンも書かなければならない
.text
.globl __start
__start:
jal main
li
$v0, 10
syscall
# mainを呼び出す
# exitシステムコール
課題1(〆切:6月4日(日)23時)

myexceptions.s を作成し、引数の値を10倍す
るシステムコールを作れ

SPIM では syscall 例外は発生しないので、代わりに
トラップ例外を使用する

teq $zero, $zero でトラップ例外を発生させられる
li
li
teq
$v0, 100
$a0, 55
$zero, $zero
move $a0, $v0
li
$v0, 1
syscall
# システムコール番号100
# 10倍したい値
# トラップ例外を発生させる
# 10倍した値を表示
新しいシステムコールを使うプログラム例
課題1のヒント
myexceptions.s
.kdata
:
.ktext 0x80000180
# 例外ハンドラで使用するレジスタを退避($atと$t0など)
# Causeレジスタから例外番号を取得
# 例外番号が13以外ならdoneにジャンプ
# システムコール番号($v0)が100以外ならdoneにジャンプ
# 引数($a0)の値を10倍(mul命令)して$v0に代入
done:
# EPCレジスタを4進める
# 退避したレジスタを復帰
eret
:
__start:
:
課題2

caller-save で書かれた次ページのコードを calleesave で書き直し、コードの効率を論じよ



0 または 1 を 8 回入力させて 8 ビットの2進数とみなし、それ
を 10 進数に変換するプログラム
 入力1×27 + 入力2×26 + 入力3×25 + 入力4×24 +
入力5×23 + 入力1×22 + 入力7×21 + 入力8×20
 結果を2倍しながら入力を足していく
$s0〜$s7 の内、サブルーチン内で使わないレジスタを退避す
る必要はない
main ルーチンもサブルーチンであることに注意せよ
 main で $s0〜$s7 を使うなら、最初と最後で退避・復帰する必
要がある
2進10進変換プログラム
.text
addi $t1, $t1, 1
blt $t1, 8, loop
main:
addi $sp, $sp, -12
sw $ra, 0($sp)
li $t0, 0
li $t1, 0
# 結果
# i=0
loop:
sw $t0, 4($sp)
sw $t1, 8($sp)
jal read1bit
lw $t1, 8($sp)
lw $t0, 4($sp)
sll $t0, $t0, 1 # 2倍
add $t0, $t0, $v0
move $a0, $t0
li $v0, 1
syscall
lw $ra, 0($sp)
addi $sp, $sp, 12
jr $ra
# サブルーチン
read1bit:
li $v0, 5
syscall # read_int
jr $ra
課題3 (オプション)
乗算を行うサブルーチンを mul 命令を使わず
に作成せよ


単純なアルゴリズム(筆算と同じアルゴリズム)
かける数の最下位ビットが1なら
かけられる数を結果に足す
1.

かけられる数を1ビット左シフト
2.

sll $x, $x, 1
かける数を1ビット右シフト
3.

4.
andi $x, $y, 0x1(最下位ビットを得る)
srl $x, $x, 1
かける数のビット数回繰り返す
0011
× 0110
0000
0011
0011
+ 0000
0010010
課題3の難易度

以下の順に難しくなるので、できるところまでや
ればよい




16ビットの乗算(結果は32ビット)
より高速なアルゴリズム
32ビット×32ビット(結果は64ビット)
より多いビット数の乗算
注意

~/Exercise/compsys/04/ で作業すること
できるだけ感想も書いて下さい

5/22 は休講です
