1 / 24

マイクロプロセッサ

マイクロプロセッサ. 第4回. 命令:マシンの言葉(その2). 関根 優年. コンピュータハードウェア内での手続きのサポート. 手続き、サブルーチン( C でいえば関数)はどうするのか?. 手続きを行うときの手順. 手続き終了後には、何も なかったように復帰可能とする. 1.手続きからアクセスできる場所にパラメータを置く 2.手続きに制御を移す 3.手続きに必要なメモリ資源を入手する 4.必要なタスクを実行する 5 . 呼び出し元のプログラムからアクセスできる   場所に結果を置く 6.制御を元の位置に戻す. 呼び出し元のプログラム のメモリ領域.

kaseem-bond
Download Presentation

マイクロプロセッサ

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. マイクロプロセッサ 第4回 命令:マシンの言葉(その2) 関根 優年

  2. コンピュータハードウェア内での手続きのサポートコンピュータハードウェア内での手続きのサポート 手続き、サブルーチン(Cでいえば関数)はどうするのか? 手続きを行うときの手順 手続き終了後には、何も なかったように復帰可能とする 1.手続きからアクセスできる場所にパラメータを置く 2.手続きに制御を移す 3.手続きに必要なメモリ資源を入手する 4.必要なタスクを実行する 5. 呼び出し元のプログラムからアクセスできる   場所に結果を置く 6.制御を元の位置に戻す 呼び出し元のプログラム のメモリ領域 手続き用のレジスタ領域  パラメータ、結果などの保持 プロセッサ 算術 演算 手続きプログラム のメモリ領域 手続き用のメモリ領域確保 レジスタ

  3. 手続き読出しのためのレジスタ・命令 Cでいえば、 int func(int a,int b) // 引数 { : return xxx;// 返り値 } 手続き読み出しのためのレジスタ ・$a0~$a3 : 4個の引数レジスタ、パラメータを渡すために使用 ・$v0~$v1 : 2個の返り値レジスタ、結果の値を返すために使用 ・$ra : 1個の戻りアドレス・レジスタ、制御を元に戻すために使用 ①手続き用の引数を$a0~$a3に渡す ②手続きを開始する命令 : jal(jump and link)命令 手続きのアドレスにジャンプすると同時に、 次の命令のアドレスをレジスタ$raに保持する jal ProcedureAddress (PC+4をレジスタ$raに退避させる) PC(プログラムカウンタ)-現在実行中の命令アドレスが保持されるレジスタ ③手続き本体 ④手続きの返り値を$v0~$v1に渡す ⑤手続きを終了する jr $ra 次の命令アドレス(レジスタ$raに格納されている)へジャンプ

  4. もっと多くのレジスタを使用する場合 メモリにレジスタを スピルアウト(退避)  手続きのための引数、戻り値に必要とされる レジスタ数がそれぞれ4個,2個を超えたらば? スタック プッシュ(push)  スタックに  データを入れる スタックポインタ を1語分減少 メモリ New reg Old reg : : : : : レジスタ の書込 Old reg : : : : : スタックポインタ (退避先のメモリ アドレス位置) スタック 領域 ポップ(pop)  スタックに  データを出す スタックポインタ を1語分増加 スタックポインタを動かし、 レジスタを退避するデータ構造 : : : : : レジスタ の読出 MIPSアーキテクチャでは、スタック ポインタ専用のレジスタ$spがある

  5. 例題) 他の手続きを呼出さない手続きのコンパイル例題) 他の手続きを呼出さない手続きのコンパイル 例題) 次のC言語のステートメントを、MIPSコードに変換せよ int leaf_example (int g, int h, int i, int j){ int f; f = (g + h) - (i + j); return f; } なお、4,8,12といった定数を直接加算 または減算ができるとする 答) パラメータ変数 g, h, i, jは引数レジスタ$a0,$a1,$a2,$a3、fは$s0とする Leaf_example : sub$sp,$sp,12 # スタックに3語分のスペースをとる sw $t1,8($sp) # レジスタ$t1の値を退避 sw $t0,4($sp) # レジスタ$t0の値を退避 sw $s0,0($sp) # レジスタ$s0の値を退避 add $t0,$a0,$a1# g+hが$t0に記憶 add $t1,$a2,$a3 # i+jを$t1に記憶 sub $s0,$t1,$t2 # 変数fに$t0-$t1を記憶 add $v0,$s0,$zero #fを返す プッシュ操作 関数の中での計算 ポップ操作

  6. 例題) 他の手続きを呼出さない手続きのコンパイル-2例題) 他の手続きを呼出さない手続きのコンパイル-2 lw $s0,0($sp) # レジスタ$s0の値を復元 lw $t0,4($sp) # レジスタ$t0の値を復元 lw $t1,8($sp) # レジスタ$t1の値を復元 add $sp,$sp,12 # スタックから3語分のスペースを除く jr $ra # 呼び出し元のルーチンにジャンプして戻る ここで$t0~$t7は手続き時に破壊されてもよいとした

  7. 入れ子にされた手続き 手続き内に、他の手続きさらには自分自身がいる場合はどうする? まともにやると、   手続き用の引数レジスタ $a0~$a3   関数内で計算に用いられているレジスタ 戻りアドレスレジスタ $ra が競合してしまう。 手続きの呼び出し元 に復帰不能になる 保存されるべきほかの全てのレジスタ をスタック上にプッシュする 呼び出し側 : 呼び出し後に必要となる引数レジスタ$a0~$a3、一時レジスタ $t0~$t7があれば、プッシュ 被呼び出し側: 戻りアドレスレジスタ$ra及び被呼び出し方で使用する退避レジ           スタ$s0~$s7をプッシュ。 スタックポインタの$spを、スタック中に格納されるレジスタに設定 手続きから戻るときは、レジスタをメモリから復元し、スタック・ポインタを再調整する

  8. 例題) 入れ子状にリンクされた、再帰型手続きのコンパイル例題) 入れ子状にリンクされた、再帰型手続きのコンパイル 例題) 次の階乗計算のC言語のステートメントを、MIPSコードに変換せよ int fact (int n){ if(n < 1) return (1); else return (n*fact(n-1)); } 答) パラメータ変数nは引数レジスタ$a0に相当する fact : sub$sp,$sp,8 # スタックに2語分のスペースをとる sw $ra,4($sp) # 戻りアドレスの退避 sw $a0,0($sp) # 引数レジスタ$a0の退避 slt $t0,$a0,1 # n<1であるかをチェック beq $t0,$zero,L1 # n≧1であれば、L1に分岐 add $v0,$zero,1# $v0に1を返す. add $sp,$sp,8 # スタックを2語分のスペースを落とす jr $ra # 呼び出し元に戻る L1: sub $a0,$a0,1 # n=n-1 jal fact # 引数n-1を使ってfactを呼び出す lw $a0,0($sp) # 引数nの復元 lw $ra,4($sp) # 戻りアドレスを復元する add $sp,$sp,8 # スタックから2語分のスペースをおとす mul $v0,$a0,$v0 # n * fact(n - 1)を返す jr $ra # 呼び出し元に戻る プッシュ操作 ここで再帰 終了 ポップ操作 mul $s0,$s1,$s2 は乗算命令としている

  9. MIPSのレジスタ規約 レジスタ番号1は、アセンブラ用に予約($at) レジスタ番号26-27は、オペレーティングシステム用に予約($k0-$k1) 32個のレジスタ フレームポインタ($fp): 手続き用のデータ格納領域(手続きフレーム)のポインタ               手続き中にスタックポインタが変わる場合に有効 グローバルポインタ($gp): Cでいうグローバル変数(手続きの外で定義された                  変数)に相当するレジスタ

  10. 数値を超えて アルファベット文字 ー ASCIIコード(7bit)  (American Standard Code for Information Interchange) 文字は1バイト(8bit)単位で扱う byte転送用コマンド load byte : メモリから1バイト読込 lb $t0,0($sp) store byte : メモリに1バイト書込 sb $t0,0($sp) 文字列 : 文字が並んだもの 自然言語をほとんど 網羅できるユニコード(16ビット) などもある

  11. 例題) 文字列コピー手続きのコンパイル 例題) 文字列コピーを表すC言語のステートメントを、MIPSコードに変換せよ void strcpy (char x[], char y[]){ int i i = 0; while((x[i]=y[i])!=0) i=i+1; } ただし、文字列の終端には null文字があるとする。 答) 配列x,yのベースアドレスは引数レジスタ$a0,$a1, iは$s0とする。 strcpy : sub$sp,$sp,4 # スタックに1語分のスペースをとる sw $s0,0($sp) # レジスタ$s0の退避 add $s0,$zero,$zero # i=0 L1: add $t1,$a1,$s0 # y[i]のアドレスを$t1に(バイトなのでiのまま) lb $t2,0($t1) # y[i]の値を$t2に add $t3,$a0,$s0 # x[i]のアドレスを$t3に sb $t2,0($t3) # x[i]=y[i] add $s0,$s0,1 # i=i+1 bne $t2,$zero,L1# もし、y[i]がヌル文字(0)でなければL1へ lw $s0,0($sp) # $s0の前の値を復元する add $sp,$sp,4 # スタックから1語分のスペースをおとす jr $ra # 呼び出し元に戻る プッシュ操作 ポップ操作

  12. MIPSのその他のアドレシング方式 即値アドレシング方式 プログラム中において定数を使う演算が多い 例) 配列の次の要素を指すためのインデックス繰り上げ     ループの繰り返し回数のカウント     入れ子になった手続き呼出しにおけるスタック調整 全体の演算に対する割合 gcc(Cコンパイラ) 52% spice(回路シミュレータ) 69% これまでのMIPS命令では 例) $spに4を足す演算 lw $t0,Addrconstant($s0) # $t0=4 add $sp,$sp,$t0 # $sp=$sp+$t0 メモリから定数を取り出さずに、計算できるとよい 即値命令 :    オペランドの一つを定数とし、命令自体の中に定数を持つ

  13. 例題) アセンブリ言語の定数の機械語への翻訳例題) アセンブリ言語の定数の機械語への翻訳 例題) 次の加算の即値命令addi(add immediate)をMIPSの機械語命令 を記述せよ addi $sp,$sp,4 答) 上記の命令の機械語コードは以下のとおり op rs rt immediate 10進数 2進数 即値で表現できるのは16ビット set less than命令(immediate) slti $t0,$s2,10 # $s2<10のとき、$t0=1とする

  14. load upper immediate命令 設計原則4 : 一般的な場合は高速化せよ オペランドが定数である演算は多数 ⇒ メモリ読み出しなしでの即値演算 定数が16ビット以上の場合の対応 load upper immediate命令 定数の上位16ビットを格納する即値命令 lui $t0,255 の機械語 即値がレジスタの上位ビットにコピー lui $t0,255の実行後のレジスタ$t0の内容

  15. 例題) 32ビットの定数のロード 例題) 次の32ビットの定数をレジスタ$s0にロードするMIPSコードを記述せよ 0000 0000 0011 1101 0000 1001 0000 0000 答) 上位16ビットを$s0にロード lui $s0,61 (上位16ビット0000 0000 0011 1101は10進数で61) 下位16ビットを$s0に追加(実際には即値で加算) addi $s0,$s0,2304 (下位16ビット0000 1001 0000 0000は10進数で2304)

  16. 分岐とジャンプにおけるアドレシング j 10000 # 10000番地にジャンプ ジャンプ命令 6ビット 26ビット 226ビットのフィールドへ分岐可能  条件分岐 命令 bne $s0,$s1,Exit # $s0と$s1が等しくないときExitへ 6ビット 5ビット 5ビット 16ビット 216ビットのフィールドへ条件分岐可能  メモリ領域の拡大に伴い、プログラムが巨大化 PC相対アドレシング方式 分岐可能なアドレス領域を拡大する方法

  17. PC相対アドレシング方式 分岐先は、分岐命令の近くにあることが多い 現在の命令アドレス(プログラムカウンタ)位置を基準に分岐する 分岐先のアドレス=分岐命令中のレジスタ値+現在のプログラムカウンタ プログラムカウンタから±215語、相対的にアドレスを動かすことが可能 メモリ MIPSにおいても、条件分岐命令について PC相対アドレシング方式を採用 現在の プログラムカウンタ 分岐命令 ジャンプ命令(手続き呼び出しなど) PCの近くにジャンプするとは限らない   ⇒ 絶対値によるアドレシング この相対的なアドレス値 を分岐命令時に指定 分岐先の アドレス MIPSでは、 (PC+4)のアドレスを基準に相対アドレシング。 相対アドレス値は語数で表す(1語=4バイト)

  18. 例題) 機械語での分岐オフセットの表記 をコンパイルしたときの次のMIPSコード を機械語表現せよ 例題) while (save[i] == k) i = i + j; Loop: add $t1,$s3,$s3 # ここは前の例題で $s3 ← i add $t1,$t1,$t1 # 出ているので省略 add $t1,$t1,$s5# lw $t0,0($t1) #save[i]をt0に格納 bne $t0,$s5,Exit # save[i]≠kでExitへ add $s3,$s3,$s4 # i=i+j j Loop # Loopへ Exit: ただし、ループの開始アドレスを80000とする 答) 80000 80004 ここが 条件分岐命令 80008 80012 80016 (PC+4)=80020 80020 に2語長つまり、 8足したアドレス (80028)へ分岐 80024 80028 分岐先のアドレス

  19. 例題) より遠くへの分岐 分岐先が216ビット以上にあるとき   ⇒ 分岐先への無条件分岐を挿入し、       挿入したジャンプ先に飛ばすかどうかを条件分岐する 例題) レジスタ$s0とレジスタ$s1が等しいときに分岐する以下の命令 beq $s0,$s1,L1 をもっと遠くへ分岐可能とする命令列に置換えよ 答) bne $s0,$s1,L2# $s0≠$s1の場合は、L2へ分岐 j L1 # $s0=$s1は、L1へ分岐 L2:

  20. MIPSのアドレシング・モードのまとめ(1) 1.即値アドレシング オペランドに命令中の定数を指定 2.レジスタアドレシング オペランドにレジスタを指定 3.ベース相対アドレシング 命令中の定数とレジスタの和で、オペランド が記憶されているメモリアドレスを指定 命令中の定数とPCの和で、オペランド が記憶されているメモリアドレスを指定 4.PC相対アドレシング 5.擬似直接アドレシング 命令中の26ビットとPCの上位ビットを 連結してジャンプアドレスを指定

  21. MIPSのアドレシング・モードのまとめ(2) 1.即値アドレシング 2.レジスタアドレシング レジスタ 3.ベース相対アドレシング メモリ Byte / Half Word / Word + 4.PC相対アドレシング メモリ Word +

  22. プログラムの起動 Cプログラム  配列とポインタ:  アセンブリコードでの両者の違いは? コンパイラ 例) 配列を0クリアする  アセンブリ言語プログラム  アセンブラ オブジェクト:機械語モジュール オブジェクト:ライブラリルーチン リンカ 実行ファイル:機械語プログラム ローダ  メモリ 

  23. 配列バージョンのclear clear1( int array[], int size ){ int i; for( i=0; i<size; i=i+1 ) array[i]=0; } arrayは$a0, sizeは$a1にiは$t0にあるとする # i($t0)に0($zero)をコピー : move $t0,$t1($t1を$t0にコピー) move $t0,$zero # i = 0 loop1: add $t1,$t0,$t0 # $t1=i*2 add $t1,$t1,$t1 # $t1=i*4 # array[i]のアドレス値を$t2に格納 add $t2,$a0,$t1 # $t2=array[i]のアドレス # メモリ領域(アドレス array[i])に、0を格納する sw $zero,0($t2) # array[i]=0 addi $t0,$t0,1 # i=i+1 # iとsizeの比較を行い、ループの終了判定を行う($t3が比較結果) slt $t3,$t0,$a1 # (i<size)なら$t3=1 bne $t3,$zero,loop1 # (i<size)ならloop1へ ループ内でiの繰り上げを計算し、毎回アドレスを計算しなおす

  24. ポインタバージョンのclear clear2( int *array, int size ){ int *p; for( p=&array[0]; p<&array[size]; p=p+1 ) *p=0; } arrayの開始アドレスは$a0, sizeは$a1にあり、pは$t0とする # pに置くアドレス値を$a0からコピー move $t0,$a0 # p=&array[0] # array[size]のアドレスを計算し、$t2に格納 add $t1,$a1,$a1 # $t1=size*2 add $t1,$t1,$t1 # $t1=size*4 add $t2,$a0,$t1 # $t2=array[size]のアドレス # 現在のアドレス($t0に格納)のメモリ値を0にする。 loop2: sw $zero,0($t0) # メモリ[p]=0 # $t0を4バイト(1語長)進ませる addi $t0,$t0,4 # p=p+4 # iとsizeの比較を行い、ループの終了判定を行う($t3が比較結果) slt $t3,$t0,$t2 # (p<&array[size])なら$t3=1 bne $t3,$zero,loop2 # (p<&array[size])ならloop2へ アドレスのポインタを直接繰り上げ ⇒ ループ内の命令数が7⇒4へ

More Related