170 likes | 301 Views
計算機構成 第 7 回 サブルーチンコールとスタック テキスト p85-90. 情報工学科 天野英晴. サブルーチンコール. メインルーチン. A. サブルーチンは呼ばれた所に 戻っていく 違った場所で呼べる ソフトウェアのモジュール化が可能 現在のプログラムでは不可欠. call A. return. call A. サブルーチンコール命令の 実装における選択 →戻り番地( pc+1) を どこにしまうか?. Jump and Link. 戻り番地を最大番号のレジスタに保存 POCO の場合 r7 古典的な手法でマインフレーム時代に使われた
E N D
計算機構成 第7回サブルーチンコールとスタックテキストp85-90計算機構成 第7回サブルーチンコールとスタックテキストp85-90 情報工学科 天野英晴
サブルーチンコール メインルーチン A サブルーチンは呼ばれた所に 戻っていく 違った場所で呼べる ソフトウェアのモジュール化が可能 現在のプログラムでは不可欠 call A return call A サブルーチンコール命令の 実装における選択 →戻り番地(pc+1)を どこにしまうか?
Jump and Link • 戻り番地を最大番号のレジスタに保存 • POCOの場合r7 • 古典的な手法でマインフレーム時代に使われた • Branch and Link命令 • RISCで最も良く使われる方式 JAL X : pc←pc+1+X, r7←pc+1 10101 XXXXXXXX 飛ぶ範囲はJMPと同じく11ビット(-1024~1023) 議論1:サブルーチンの入れ子(ネスト)に対応しない 議論2:r7にしまうのは命令の直交性を損ねる(格好わるい)
2乗を計算する例 LDIU r0,#0 LD r1,(r0) MV r2,r1r1とr2に同じ値をセットする JAL mult end: JMP end // Subroutine Mult r3 ←r1×r2 ここでr2は破壊される mult: LDIU r3,#0 loop: ADD r3,r1 ADDI r2,#-1 BNZ r2, loop JR r7
POCOのサブルーチンコール メインルーチン A リターン命令にはJR r7が利用可能 ここで疑問: サブルーチンの中で別のサブルーチン を呼ぶとr7が壊れて戻れなくなるのでは? その通り! しかし他にも壊れては困るレジスタは あるはず →保存するためにスタックが必要 JAL B? JAL A JR r7 JAL A
スタック データを積む棚 push操作でデータを積み pop操作で取り出す • LIFO(LastIn First Out)、FILO(First In Last Out)とも呼ばれる • 演算スタックとは違う(誤解しないで!) • 主記憶上にスタック領域が確保される C C push pop C B B B A A A
Bでは r0,r1が 壊れる スタックを使った入れ子構造への対応 B メインルーチン push r0, push r1 A r1 push r0, push r7 r0 r7 JAL B r0 r7 pop r1, pop r0 JAL A r0 JAL C JR r7 pop r7, pop r0 JR r7 Cでは r1,r2が 壊れる JAL A Aでは r0,r7が 壊れる C push r1, push r2 r2 r1 r7 壊れるレジスタをサブルーチンの 入り口でpushして保存し、 出口でpopして復帰 → 呼ぶ方で保存する方法もある r0 pop r2, pop r1 JR r7
スタックの実現(push) • r6をスタックポインタとする • スタックポインタをマイナスしてからSTする push 0、push 1は ADDI r6,#-1 ST r0,(r6) ADDI r6,#-1 ST r1,(r6) r1 r0 r6 r1 スタック領域 r0 r6
スタックの実現(pop) • スタック領域はメモリの番地の小さい方に伸びる→昔からの習慣 • pop操作は、LDしてからスタックポインタを+する。 pop 1、pop 0は LD r1(r6) ADDI r6,#1 LD r0,(r6) ADDI r6,#1 r1 r6 r1 r0 r0 r6
2乗を計算する例 r2を破壊しないサブルーチンコール r6はメインルーチンで初期化する必要がある // Subroutine Mult r3 ←r1×r2 mult: ADDI r6,#-1 ST r2,(r6) LDIU r3,#0 loop: ADD r3,r1 ADDI r2,#-1 BNZ r2, loop LD r2,(r6) ADDI r6,#1 JR r7
JALを巡る議論 • 戻り番地を汎用レジスタに格納する方針 • どっちみちスタックに汎用レジスタにしまう • ならば入れ子になるときには、r7もついでにしまってやれば良い • システムスタックを持っていてCall時にこれにしまう方法(IA32などの方法)と比べて劣ってはいない→むしろ不必要なメモリ読み書きが減る • ではr7に決めちゃうのはどうなの? • 任意のレジスタにしまうことができても意味がない • JALはできるだけ遠くに飛びたいのでレジスタのフィールドはないほうが良い • 多少の格好の悪さは我慢しよう!
JAL命令のVerilog記述 • pcをr7に保存する assign rf_c = ld_op ? ddatin: jal_op ? pc+1: alu_y; assign rwe = ld_op | alu_op…| jal_op; assign cadr = jal_op? 3’b111 : rd; rfile rfile_1(.clk(clk), .a(rf_a), .aadr(rd), .b(rf_b), .badr(rs), .cadr(cadr), .we(rwe); 今までと異なりレジスタファイルのaadr=cadrではなくなる。この二つを分離する必要がある
JALのVerilog記述 飛び方はJMPと同じ always @(posedge clk or negedge rst_n) begin if(!rst_n) pc <= 0; else if ((bez_op & rf_a == 16’b0) | (bnz_op & rf_a != 16’b0) | (bpl_op & ~rf_a[15]) | (bmi_op & rf_a[15])) pc<=pc+{{8imm[7]}},imm}+1; else if (jmp_op| jal_op) pc <= pc +{{5{idatain[10]}},idatain[10:0]}}+1; else if(jr_op) pc <= rf_a; else pc<=pc+1; end
演習 1.multを利用して0番地の数の3乗を計算せよ 2.JALRrdは、JAL同様r7に戻り番地をしまってJR同様rdの中身の番地にそのまま飛ぶ命令である。これを実装せよ。 JALRrd00000ddd---11000